selftests: xsk: Put the same buffer only once in the fill ring
[linux-2.6-microblaze.git] / tools / testing / selftests / bpf / xdpxceiver.c
index 79cf082..c5c68b8 100644 (file)
@@ -19,7 +19,7 @@
  * Virtual Ethernet interfaces.
  *
  * For each mode, the following tests are run:
- *    a. nopoll - soft-irq processing
+ *    a. nopoll - soft-irq processing in run-to-completion mode
  *    b. poll - using poll() syscall
  *    c. Socket Teardown
  *       Create a Tx and a Rx socket, Tx from one socket, Rx on another. Destroy
  *       Configure sockets at indexes 0 and 1, run a traffic on queue ids 0,
  *       then remove xsk sockets from queue 0 on both veth interfaces and
  *       finally run a traffic on queues ids 1
+ *    g. unaligned mode
+ *    h. tests for invalid and corner case Tx descriptors so that the correct ones
+ *       are discarded and let through, respectively.
+ *    i. 2K frame size tests
  *
  * Total tests: 12
  *
@@ -112,13 +116,10 @@ static void __exit_with_error(int error, const char *file, const char *func, int
 
 #define exit_with_error(error) __exit_with_error(error, __FILE__, __func__, __LINE__)
 
-#define print_ksft_result(void)\
-       (ksft_test_result_pass("PASS: %s %s %s%s%s%s\n", configured_mode ? "DRV" : "SKB",\
-                              test_type == TEST_TYPE_POLL ? "POLL" : "NOPOLL",\
-                              test_type == TEST_TYPE_TEARDOWN ? "Socket Teardown" : "",\
-                              test_type == TEST_TYPE_BIDI ? "Bi-directional Sockets" : "",\
-                              test_type == TEST_TYPE_STATS ? "Stats" : "",\
-                              test_type == TEST_TYPE_BPF_RES ? "BPF RES" : ""))
+#define mode_string(test) (test)->ifobj_tx->xdp_flags & XDP_FLAGS_SKB_MODE ? "SKB" : "DRV"
+
+#define print_ksft_result(test)                                                \
+       (ksft_test_result_pass("PASS: %s %s\n", mode_string(test), (test)->name))
 
 static void memset32_htonl(void *dest, u32 val, u32 size)
 {
@@ -235,7 +236,7 @@ static void gen_udp_csum(struct udphdr *udp_hdr, struct iphdr *ip_hdr)
            udp_csum(ip_hdr->saddr, ip_hdr->daddr, UDP_PKT_SIZE, IPPROTO_UDP, (u16 *)udp_hdr);
 }
 
-static int xsk_configure_umem(struct xsk_umem_info *umem, void *buffer, u64 size, int idx)
+static int xsk_configure_umem(struct xsk_umem_info *umem, void *buffer, u64 size)
 {
        struct xsk_umem_config cfg = {
                .fill_size = XSK_RING_PROD__DEFAULT_NUM_DESCS,
@@ -246,6 +247,9 @@ static int xsk_configure_umem(struct xsk_umem_info *umem, void *buffer, u64 size
        };
        int ret;
 
+       if (umem->unaligned_mode)
+               cfg.flags |= XDP_UMEM_UNALIGNED_CHUNK_FLAG;
+
        ret = xsk_umem__create(&umem->umem, buffer, size,
                               &umem->fq, &umem->cq, &cfg);
        if (ret)
@@ -255,19 +259,6 @@ static int xsk_configure_umem(struct xsk_umem_info *umem, void *buffer, u64 size
        return 0;
 }
 
-static void xsk_populate_fill_ring(struct xsk_umem_info *umem)
-{
-       int ret, i;
-       u32 idx = 0;
-
-       ret = xsk_ring_prod__reserve(&umem->fq, XSK_RING_PROD__DEFAULT_NUM_DESCS, &idx);
-       if (ret != XSK_RING_PROD__DEFAULT_NUM_DESCS)
-               exit_with_error(-ret);
-       for (i = 0; i < XSK_RING_PROD__DEFAULT_NUM_DESCS; i++)
-               *xsk_ring_prod__fill_addr(&umem->fq, idx++) = i * umem->frame_size;
-       xsk_ring_prod__submit(&umem->fq, XSK_RING_PROD__DEFAULT_NUM_DESCS);
-}
-
 static int xsk_configure_socket(struct xsk_socket_info *xsk, struct xsk_umem_info *umem,
                                struct ifobject *ifobject, u32 qid)
 {
@@ -279,17 +270,11 @@ static int xsk_configure_socket(struct xsk_socket_info *xsk, struct xsk_umem_inf
        cfg.rx_size = xsk->rxqsize;
        cfg.tx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS;
        cfg.libbpf_flags = 0;
-       cfg.xdp_flags = xdp_flags;
-       cfg.bind_flags = xdp_bind_flags;
-
-       if (test_type != TEST_TYPE_BIDI) {
-               rxr = (ifobject->fv.vector == rx) ? &xsk->rx : NULL;
-               txr = (ifobject->fv.vector == tx) ? &xsk->tx : NULL;
-       } else {
-               rxr = &xsk->rx;
-               txr = &xsk->tx;
-       }
+       cfg.xdp_flags = ifobject->xdp_flags;
+       cfg.bind_flags = ifobject->bind_flags;
 
+       txr = ifobject->tx_on ? &xsk->tx : NULL;
+       rxr = ifobject->rx_on ? &xsk->rx : NULL;
        return xsk_socket__create(&xsk->xsk, ifobject->ifname, qid, umem->umem, rxr, txr, &cfg);
 }
 
@@ -343,7 +328,8 @@ static bool validate_interface(struct ifobject *ifobj)
        return true;
 }
 
-static void parse_command_line(struct test_spec *test, int argc, char **argv)
+static void parse_command_line(struct ifobject *ifobj_tx, struct ifobject *ifobj_rx, int argc,
+                              char **argv)
 {
        struct ifobject *ifobj;
        u32 interface_nb = 0;
@@ -361,9 +347,9 @@ static void parse_command_line(struct test_spec *test, int argc, char **argv)
                switch (c) {
                case 'i':
                        if (interface_nb == 0)
-                               ifobj = test->ifobj_tx;
+                               ifobj = ifobj_tx;
                        else if (interface_nb == 1)
-                               ifobj = test->ifobj_rx;
+                               ifobj = ifobj_rx;
                        else
                                break;
 
@@ -397,16 +383,21 @@ static void __test_spec_init(struct test_spec *test, struct ifobject *ifobj_tx,
 
                ifobj->umem = &ifobj->umem_arr[0];
                ifobj->xsk = &ifobj->xsk_arr[0];
-
-               if (i == tx)
-                       ifobj->fv.vector = tx;
-               else
-                       ifobj->fv.vector = rx;
+               ifobj->use_poll = false;
+               ifobj->pkt_stream = test->pkt_stream_default;
+
+               if (i == 0) {
+                       ifobj->rx_on = false;
+                       ifobj->tx_on = true;
+               } else {
+                       ifobj->rx_on = true;
+                       ifobj->tx_on = false;
+               }
 
                for (j = 0; j < MAX_SOCKETS; j++) {
                        memset(&ifobj->umem_arr[j], 0, sizeof(ifobj->umem_arr[j]));
                        memset(&ifobj->xsk_arr[j], 0, sizeof(ifobj->xsk_arr[j]));
-                       ifobj->umem_arr[j].num_frames = DEFAULT_PKT_CNT / 4;
+                       ifobj->umem_arr[j].num_frames = DEFAULT_UMEM_BUFFERS;
                        ifobj->umem_arr[j].frame_size = XSK_UMEM__DEFAULT_FRAME_SIZE;
                        ifobj->xsk_arr[j].rxqsize = XSK_RING_CONS__DEFAULT_NUM_DESCS;
                }
@@ -414,12 +405,33 @@ static void __test_spec_init(struct test_spec *test, struct ifobject *ifobj_tx,
 
        test->ifobj_tx = ifobj_tx;
        test->ifobj_rx = ifobj_rx;
+       test->current_step = 0;
+       test->total_steps = 1;
+       test->nb_sockets = 1;
 }
 
 static void test_spec_init(struct test_spec *test, struct ifobject *ifobj_tx,
-                          struct ifobject *ifobj_rx)
+                          struct ifobject *ifobj_rx, enum test_mode mode)
 {
+       struct pkt_stream *pkt_stream;
+       u32 i;
+
+       pkt_stream = test->pkt_stream_default;
        memset(test, 0, sizeof(*test));
+       test->pkt_stream_default = pkt_stream;
+
+       for (i = 0; i < MAX_INTERFACES; i++) {
+               struct ifobject *ifobj = i ? ifobj_rx : ifobj_tx;
+
+               ifobj->xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
+               if (mode == TEST_MODE_SKB)
+                       ifobj->xdp_flags |= XDP_FLAGS_SKB_MODE;
+               else
+                       ifobj->xdp_flags |= XDP_FLAGS_DRV_MODE;
+
+               ifobj->bind_flags = XDP_USE_NEED_WAKEUP | XDP_COPY;
+       }
+
        __test_spec_init(test, ifobj_tx, ifobj_rx);
 }
 
@@ -428,6 +440,17 @@ static void test_spec_reset(struct test_spec *test)
        __test_spec_init(test, test->ifobj_tx, test->ifobj_rx);
 }
 
+static void test_spec_set_name(struct test_spec *test, const char *name)
+{
+       strncpy(test->name, name, MAX_TEST_NAME_SIZE);
+}
+
+static void pkt_stream_reset(struct pkt_stream *pkt_stream)
+{
+       if (pkt_stream)
+               pkt_stream->rx_pkt_nb = 0;
+}
+
 static struct pkt *pkt_stream_get_pkt(struct pkt_stream *pkt_stream, u32 pkt_nb)
 {
        if (pkt_nb >= pkt_stream->nb_pkts)
@@ -436,29 +459,105 @@ static struct pkt *pkt_stream_get_pkt(struct pkt_stream *pkt_stream, u32 pkt_nb)
        return &pkt_stream->pkts[pkt_nb];
 }
 
-static struct pkt_stream *pkt_stream_generate(struct xsk_umem_info *umem, u32 nb_pkts, u32 pkt_len)
+static struct pkt *pkt_stream_get_next_rx_pkt(struct pkt_stream *pkt_stream)
+{
+       while (pkt_stream->rx_pkt_nb < pkt_stream->nb_pkts) {
+               if (pkt_stream->pkts[pkt_stream->rx_pkt_nb].valid)
+                       return &pkt_stream->pkts[pkt_stream->rx_pkt_nb++];
+               pkt_stream->rx_pkt_nb++;
+       }
+       return NULL;
+}
+
+static void pkt_stream_delete(struct pkt_stream *pkt_stream)
+{
+       free(pkt_stream->pkts);
+       free(pkt_stream);
+}
+
+static void pkt_stream_restore_default(struct test_spec *test)
+{
+       if (test->ifobj_tx->pkt_stream != test->pkt_stream_default) {
+               pkt_stream_delete(test->ifobj_tx->pkt_stream);
+               test->ifobj_tx->pkt_stream = test->pkt_stream_default;
+       }
+       test->ifobj_rx->pkt_stream = test->pkt_stream_default;
+}
+
+static struct pkt_stream *__pkt_stream_alloc(u32 nb_pkts)
 {
        struct pkt_stream *pkt_stream;
-       u32 i;
 
-       pkt_stream = malloc(sizeof(*pkt_stream));
+       pkt_stream = calloc(1, sizeof(*pkt_stream));
        if (!pkt_stream)
-               exit_with_error(ENOMEM);
+               return NULL;
 
        pkt_stream->pkts = calloc(nb_pkts, sizeof(*pkt_stream->pkts));
-       if (!pkt_stream->pkts)
+       if (!pkt_stream->pkts) {
+               free(pkt_stream);
+               return NULL;
+       }
+
+       pkt_stream->nb_pkts = nb_pkts;
+       return pkt_stream;
+}
+
+static struct pkt_stream *pkt_stream_generate(struct xsk_umem_info *umem, u32 nb_pkts, u32 pkt_len)
+{
+       struct pkt_stream *pkt_stream;
+       u32 i;
+
+       pkt_stream = __pkt_stream_alloc(nb_pkts);
+       if (!pkt_stream)
                exit_with_error(ENOMEM);
 
        pkt_stream->nb_pkts = nb_pkts;
        for (i = 0; i < nb_pkts; i++) {
-               pkt_stream->pkts[i].addr = (i % umem->num_frames) * umem->frame_size;
+               pkt_stream->pkts[i].addr = (i % umem->num_frames) * umem->frame_size +
+                       DEFAULT_OFFSET;
                pkt_stream->pkts[i].len = pkt_len;
                pkt_stream->pkts[i].payload = i;
+
+               if (pkt_len > umem->frame_size)
+                       pkt_stream->pkts[i].valid = false;
+               else
+                       pkt_stream->pkts[i].valid = true;
        }
 
        return pkt_stream;
 }
 
+static struct pkt_stream *pkt_stream_clone(struct xsk_umem_info *umem,
+                                          struct pkt_stream *pkt_stream)
+{
+       return pkt_stream_generate(umem, pkt_stream->nb_pkts, pkt_stream->pkts[0].len);
+}
+
+static void pkt_stream_replace(struct test_spec *test, u32 nb_pkts, u32 pkt_len)
+{
+       struct pkt_stream *pkt_stream;
+
+       pkt_stream = pkt_stream_generate(test->ifobj_tx->umem, nb_pkts, pkt_len);
+       test->ifobj_tx->pkt_stream = pkt_stream;
+       test->ifobj_rx->pkt_stream = pkt_stream;
+}
+
+static void pkt_stream_replace_half(struct test_spec *test, u32 pkt_len, u32 offset)
+{
+       struct xsk_umem_info *umem = test->ifobj_tx->umem;
+       struct pkt_stream *pkt_stream;
+       u32 i;
+
+       pkt_stream = pkt_stream_clone(umem, test->pkt_stream_default);
+       for (i = 0; i < test->pkt_stream_default->nb_pkts; i += 2) {
+               pkt_stream->pkts[i].addr = (i % umem->num_frames) * umem->frame_size + offset;
+               pkt_stream->pkts[i].len = pkt_len;
+       }
+
+       test->ifobj_tx->pkt_stream = pkt_stream;
+       test->ifobj_rx->pkt_stream = pkt_stream;
+}
+
 static struct pkt *pkt_generate(struct ifobject *ifobject, u32 pkt_nb)
 {
        struct pkt *pkt = pkt_stream_get_pkt(ifobject->pkt_stream, pkt_nb);
@@ -469,6 +568,8 @@ static struct pkt *pkt_generate(struct ifobject *ifobject, u32 pkt_nb)
 
        if (!pkt)
                return NULL;
+       if (!pkt->valid || pkt->len < PKT_SIZE)
+               return pkt;
 
        data = xsk_umem__get_data(ifobject->umem->buffer, pkt->addr);
        udp_hdr = (struct udphdr *)(data + sizeof(struct ethhdr) + sizeof(struct iphdr));
@@ -483,6 +584,26 @@ static struct pkt *pkt_generate(struct ifobject *ifobject, u32 pkt_nb)
        return pkt;
 }
 
+static void pkt_stream_generate_custom(struct test_spec *test, struct pkt *pkts, u32 nb_pkts)
+{
+       struct pkt_stream *pkt_stream;
+       u32 i;
+
+       pkt_stream = __pkt_stream_alloc(nb_pkts);
+       if (!pkt_stream)
+               exit_with_error(ENOMEM);
+
+       test->ifobj_tx->pkt_stream = pkt_stream;
+       test->ifobj_rx->pkt_stream = pkt_stream;
+
+       for (i = 0; i < nb_pkts; i++) {
+               pkt_stream->pkts[i].addr = pkts[i].addr;
+               pkt_stream->pkts[i].len = pkts[i].len;
+               pkt_stream->pkts[i].payload = i;
+               pkt_stream->pkts[i].valid = pkts[i].valid;
+       }
+}
+
 static void pkt_dump(void *pkt, u32 len)
 {
        char s[INET_ADDRSTRLEN];
@@ -520,9 +641,9 @@ static void pkt_dump(void *pkt, u32 len)
        fprintf(stdout, "---------------------------------------\n");
 }
 
-static bool is_pkt_valid(struct pkt *pkt, void *buffer, const struct xdp_desc *desc)
+static bool is_pkt_valid(struct pkt *pkt, void *buffer, u64 addr, u32 len)
 {
-       void *data = xsk_umem__get_data(buffer, desc->addr);
+       void *data = xsk_umem__get_data(buffer, addr);
        struct iphdr *iphdr = (struct iphdr *)(data + sizeof(struct ethhdr));
 
        if (!pkt) {
@@ -530,19 +651,24 @@ static bool is_pkt_valid(struct pkt *pkt, void *buffer, const struct xdp_desc *d
                return false;
        }
 
+       if (len < PKT_SIZE) {
+               /*Do not try to verify packets that are smaller than minimum size. */
+               return true;
+       }
+
+       if (pkt->len != len) {
+               ksft_test_result_fail
+                       ("ERROR: [%s] expected length [%d], got length [%d]\n",
+                        __func__, pkt->len, len);
+               return false;
+       }
+
        if (iphdr->version == IP_PKT_VER && iphdr->tos == IP_PKT_TOS) {
                u32 seqnum = ntohl(*((u32 *)(data + PKT_HDR_SIZE)));
 
-               if (opt_pkt_dump && test_type != TEST_TYPE_STATS)
+               if (opt_pkt_dump)
                        pkt_dump(data, PKT_SIZE);
 
-               if (pkt->len != desc->len) {
-                       ksft_test_result_fail
-                               ("ERROR: [%s] expected length [%d], got length [%d]\n",
-                                       __func__, pkt->len, desc->len);
-                       return false;
-               }
-
                if (pkt->payload != seqnum) {
                        ksft_test_result_fail
                                ("ERROR: [%s] expected seqnum [%d], got seqnum [%d]\n",
@@ -574,14 +700,20 @@ static void complete_pkts(struct xsk_socket_info *xsk, int batch_size)
        unsigned int rcvd;
        u32 idx;
 
-       if (!xsk->outstanding_tx)
-               return;
-
        if (xsk_ring_prod__needs_wakeup(&xsk->tx))
                kick_tx(xsk);
 
        rcvd = xsk_ring_cons__peek(&xsk->umem->cq, batch_size, &idx);
        if (rcvd) {
+               if (rcvd > xsk->outstanding_tx) {
+                       u64 addr = *xsk_ring_cons__comp_addr(&xsk->umem->cq, idx + rcvd - 1);
+
+                       ksft_test_result_fail("ERROR: [%s] Too many packets completed\n",
+                                             __func__);
+                       ksft_print_msg("Last completion address: %llx\n", addr);
+                       return;
+               }
+
                xsk_ring_cons__release(&xsk->umem->cq, rcvd);
                xsk->outstanding_tx -= rcvd;
        }
@@ -590,11 +722,10 @@ static void complete_pkts(struct xsk_socket_info *xsk, int batch_size)
 static void receive_pkts(struct pkt_stream *pkt_stream, struct xsk_socket_info *xsk,
                         struct pollfd *fds)
 {
-       u32 idx_rx = 0, idx_fq = 0, rcvd, i, pkt_count = 0;
-       struct pkt *pkt;
+       struct pkt *pkt = pkt_stream_get_next_rx_pkt(pkt_stream);
+       u32 idx_rx = 0, idx_fq = 0, rcvd, i;
        int ret;
 
-       pkt = pkt_stream_get_pkt(pkt_stream, pkt_count++);
        while (pkt) {
                rcvd = xsk_ring_cons__peek(&xsk->rx, BATCH_SIZE, &idx_rx);
                if (!rcvd) {
@@ -622,13 +753,21 @@ static void receive_pkts(struct pkt_stream *pkt_stream, struct xsk_socket_info *
                        const struct xdp_desc *desc = xsk_ring_cons__rx_desc(&xsk->rx, idx_rx++);
                        u64 addr = desc->addr, orig;
 
+                       if (!pkt) {
+                               ksft_test_result_fail("ERROR: [%s] Received too many packets.\n",
+                                                     __func__);
+                               ksft_print_msg("Last packet has addr: %llx len: %u\n",
+                                              addr, desc->len);
+                               return;
+                       }
+
                        orig = xsk_umem__extract_addr(addr);
                        addr = xsk_umem__add_offset_to_addr(addr);
-                       if (!is_pkt_valid(pkt, xsk->umem->buffer, desc))
+                       if (!is_pkt_valid(pkt, xsk->umem->buffer, addr, desc->len))
                                return;
 
                        *xsk_ring_prod__fill_addr(&xsk->umem->fq, idx_fq++) = orig;
-                       pkt = pkt_stream_get_pkt(pkt_stream, pkt_count++);
+                       pkt = pkt_stream_get_next_rx_pkt(pkt_stream);
                }
 
                xsk_ring_prod__submit(&xsk->umem->fq, rcvd);
@@ -639,7 +778,7 @@ static void receive_pkts(struct pkt_stream *pkt_stream, struct xsk_socket_info *
 static u32 __send_pkts(struct ifobject *ifobject, u32 pkt_nb)
 {
        struct xsk_socket_info *xsk = ifobject->xsk;
-       u32 i, idx;
+       u32 i, idx, valid_pkts = 0;
 
        while (xsk_ring_prod__reserve(&xsk->tx, BATCH_SIZE, &idx) < BATCH_SIZE)
                complete_pkts(xsk, BATCH_SIZE);
@@ -654,14 +793,13 @@ static u32 __send_pkts(struct ifobject *ifobject, u32 pkt_nb)
                tx_desc->addr = pkt->addr;
                tx_desc->len = pkt->len;
                pkt_nb++;
+               if (pkt->valid)
+                       valid_pkts++;
        }
 
        xsk_ring_prod__submit(&xsk->tx, i);
-       if (stat_test_type != STAT_TEST_TX_INVALID)
-               xsk->outstanding_tx += i;
-       else if (xsk_ring_prod__needs_wakeup(&xsk->tx))
-               kick_tx(xsk);
-       complete_pkts(xsk, i);
+       xsk->outstanding_tx += valid_pkts;
+       complete_pkts(xsk, BATCH_SIZE);
 
        return i;
 }
@@ -674,23 +812,23 @@ static void wait_for_tx_completion(struct xsk_socket_info *xsk)
 
 static void send_pkts(struct ifobject *ifobject)
 {
-       struct pollfd fds[MAX_SOCKS] = { };
+       struct pollfd fds = { };
        u32 pkt_cnt = 0;
 
-       fds[0].fd = xsk_socket__fd(ifobject->xsk->xsk);
-       fds[0].events = POLLOUT;
+       fds.fd = xsk_socket__fd(ifobject->xsk->xsk);
+       fds.events = POLLOUT;
 
        while (pkt_cnt < ifobject->pkt_stream->nb_pkts) {
                u32 sent;
 
-               if (test_type == TEST_TYPE_POLL) {
+               if (ifobject->use_poll) {
                        int ret;
 
-                       ret = poll(fds, 1, POLL_TMOUT);
+                       ret = poll(&fds, 1, POLL_TMOUT);
                        if (ret <= 0)
                                continue;
 
-                       if (!(fds[0].revents & POLLOUT))
+                       if (!(fds.revents & POLLOUT))
                                continue;
                }
 
@@ -714,7 +852,7 @@ static bool rx_stats_are_valid(struct ifobject *ifobject)
        optlen = sizeof(stats);
        err = getsockopt(fd, SOL_XDP, XDP_STATISTICS, &stats, &optlen);
        if (err) {
-               ksft_test_result_fail("ERROR: [%s] getsockopt(XDP_STATISTICS) error %u %s\n",
+               ksft_test_result_fail("ERROR Rx: [%s] getsockopt(XDP_STATISTICS) error %u %s\n",
                                      __func__, -err, strerror(-err));
                return true;
        }
@@ -755,7 +893,7 @@ static void tx_stats_validate(struct ifobject *ifobject)
        optlen = sizeof(stats);
        err = getsockopt(fd, SOL_XDP, XDP_STATISTICS, &stats, &optlen);
        if (err) {
-               ksft_test_result_fail("ERROR: [%s] getsockopt(XDP_STATISTICS) error %u %s\n",
+               ksft_test_result_fail("ERROR Tx: [%s] getsockopt(XDP_STATISTICS) error %u %s\n",
                                      __func__, -err, strerror(-err));
                return;
        }
@@ -767,73 +905,63 @@ static void tx_stats_validate(struct ifobject *ifobject)
                              __func__, stats.tx_invalid_descs, ifobject->pkt_stream->nb_pkts);
 }
 
-static void thread_common_ops(struct ifobject *ifobject, void *bufs)
+static void thread_common_ops(struct test_spec *test, struct ifobject *ifobject)
 {
-       u64 umem_sz = ifobject->umem->num_frames * ifobject->umem->frame_size;
        int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE;
-       size_t mmap_sz = umem_sz;
-       int ctr = 0, ret;
+       u32 i;
 
        ifobject->ns_fd = switch_namespace(ifobject->nsname);
 
-       if (test_type == TEST_TYPE_BPF_RES)
-               mmap_sz *= 2;
+       if (ifobject->umem->unaligned_mode)
+               mmap_flags |= MAP_HUGETLB;
 
-       bufs = mmap(NULL, mmap_sz, PROT_READ | PROT_WRITE, mmap_flags, -1, 0);
-       if (bufs == MAP_FAILED)
-               exit_with_error(errno);
+       for (i = 0; i < test->nb_sockets; i++) {
+               u64 umem_sz = ifobject->umem->num_frames * ifobject->umem->frame_size;
+               u32 ctr = 0;
+               void *bufs;
 
-       while (ctr++ < SOCK_RECONF_CTR) {
-               ret = xsk_configure_umem(&ifobject->umem_arr[0], bufs, umem_sz, 0);
-               if (ret)
-                       exit_with_error(-ret);
+               bufs = mmap(NULL, umem_sz, PROT_READ | PROT_WRITE, mmap_flags, -1, 0);
+               if (bufs == MAP_FAILED)
+                       exit_with_error(errno);
 
-               ret = xsk_configure_socket(&ifobject->xsk_arr[0], &ifobject->umem_arr[0],
-                                          ifobject, 0);
-               if (!ret)
-                       break;
+               while (ctr++ < SOCK_RECONF_CTR) {
+                       int ret;
 
-               /* Retry Create Socket if it fails as xsk_socket__create() is asynchronous */
-               if (ctr >= SOCK_RECONF_CTR)
-                       exit_with_error(-ret);
-               usleep(USLEEP_MAX);
-       }
+                       ret = xsk_configure_umem(&ifobject->umem_arr[i], bufs, umem_sz);
+                       if (ret)
+                               exit_with_error(-ret);
 
-       if (test_type == TEST_TYPE_BPF_RES) {
-               ret = xsk_configure_umem(&ifobject->umem_arr[1], (u8 *)bufs + umem_sz, umem_sz, 1);
-               if (ret)
-                       exit_with_error(-ret);
+                       ret = xsk_configure_socket(&ifobject->xsk_arr[i], &ifobject->umem_arr[i],
+                                                  ifobject, i);
+                       if (!ret)
+                               break;
 
-               ret = xsk_configure_socket(&ifobject->xsk_arr[1], &ifobject->umem_arr[1],
-                                          ifobject, 1);
-               if (ret)
-                       exit_with_error(-ret);
+                       /* Retry if it fails as xsk_socket__create() is asynchronous */
+                       if (ctr >= SOCK_RECONF_CTR)
+                               exit_with_error(-ret);
+                       usleep(USLEEP_MAX);
+               }
        }
 
        ifobject->umem = &ifobject->umem_arr[0];
        ifobject->xsk = &ifobject->xsk_arr[0];
 }
 
-static bool testapp_is_test_two_stepped(void)
-{
-       return (test_type != TEST_TYPE_BIDI && test_type != TEST_TYPE_BPF_RES) || second_step;
-}
-
 static void testapp_cleanup_xsk_res(struct ifobject *ifobj)
 {
-       if (testapp_is_test_two_stepped()) {
-               xsk_socket__delete(ifobj->xsk->xsk);
-               (void)xsk_umem__delete(ifobj->umem->umem);
-       }
+       print_verbose("Destroying socket\n");
+       xsk_socket__delete(ifobj->xsk->xsk);
+       munmap(ifobj->umem->buffer, ifobj->umem->num_frames * ifobj->umem->frame_size);
+       xsk_umem__delete(ifobj->umem->umem);
 }
 
 static void *worker_testapp_validate_tx(void *arg)
 {
-       struct ifobject *ifobject = (struct ifobject *)arg;
-       void *bufs = NULL;
+       struct test_spec *test = (struct test_spec *)arg;
+       struct ifobject *ifobject = test->ifobj_tx;
 
-       if (!second_step)
-               thread_common_ops(ifobject, bufs);
+       if (test->current_step == 1)
+               thread_common_ops(test, ifobject);
 
        print_verbose("Sending %d packets on interface %s\n", ifobject->pkt_stream->nb_pkts,
                      ifobject->ifname);
@@ -842,24 +970,55 @@ static void *worker_testapp_validate_tx(void *arg)
        if (stat_test_type == STAT_TEST_TX_INVALID)
                tx_stats_validate(ifobject);
 
-       testapp_cleanup_xsk_res(ifobject);
+       if (test->total_steps == test->current_step)
+               testapp_cleanup_xsk_res(ifobject);
        pthread_exit(NULL);
 }
 
+static void xsk_populate_fill_ring(struct xsk_umem_info *umem, struct pkt_stream *pkt_stream)
+{
+       u32 idx = 0, i, buffers_to_fill;
+       int ret;
+
+       if (umem->num_frames < XSK_RING_PROD__DEFAULT_NUM_DESCS)
+               buffers_to_fill = umem->num_frames;
+       else
+               buffers_to_fill = XSK_RING_PROD__DEFAULT_NUM_DESCS;
+
+       ret = xsk_ring_prod__reserve(&umem->fq, buffers_to_fill, &idx);
+       if (ret != buffers_to_fill)
+               exit_with_error(ENOSPC);
+       for (i = 0; i < buffers_to_fill; i++) {
+               u64 addr;
+
+               if (pkt_stream->use_addr_for_fill) {
+                       struct pkt *pkt = pkt_stream_get_pkt(pkt_stream, i);
+
+                       if (!pkt)
+                               break;
+                       addr = pkt->addr;
+               } else {
+                       addr = i * umem->frame_size + DEFAULT_OFFSET;
+               }
+
+               *xsk_ring_prod__fill_addr(&umem->fq, idx++) = addr;
+       }
+       xsk_ring_prod__submit(&umem->fq, buffers_to_fill);
+}
+
 static void *worker_testapp_validate_rx(void *arg)
 {
-       struct ifobject *ifobject = (struct ifobject *)arg;
-       struct pollfd fds[MAX_SOCKS] = { };
-       void *bufs = NULL;
+       struct test_spec *test = (struct test_spec *)arg;
+       struct ifobject *ifobject = test->ifobj_rx;
+       struct pollfd fds = { };
 
-       if (!second_step)
-               thread_common_ops(ifobject, bufs);
+       if (test->current_step == 1)
+               thread_common_ops(test, ifobject);
 
-       if (stat_test_type != STAT_TEST_RX_FILL_EMPTY)
-               xsk_populate_fill_ring(ifobject->umem);
+       xsk_populate_fill_ring(ifobject->umem, ifobject->pkt_stream);
 
-       fds[0].fd = xsk_socket__fd(ifobject->xsk->xsk);
-       fds[0].events = POLLIN;
+       fds.fd = xsk_socket__fd(ifobject->xsk->xsk);
+       fds.events = POLLIN;
 
        pthread_barrier_wait(&barr);
 
@@ -867,12 +1026,10 @@ static void *worker_testapp_validate_rx(void *arg)
                while (!rx_stats_are_valid(ifobject))
                        continue;
        else
-               receive_pkts(ifobject->pkt_stream, ifobject->xsk, fds);
-
-       if (test_type == TEST_TYPE_TEARDOWN)
-               print_verbose("Destroying socket\n");
+               receive_pkts(ifobject->pkt_stream, ifobject->xsk, &fds);
 
-       testapp_cleanup_xsk_res(ifobject);
+       if (test->total_steps == test->current_step)
+               testapp_cleanup_xsk_res(ifobject);
        pthread_exit(NULL);
 }
 
@@ -880,61 +1037,46 @@ static void testapp_validate_traffic(struct test_spec *test)
 {
        struct ifobject *ifobj_tx = test->ifobj_tx;
        struct ifobject *ifobj_rx = test->ifobj_rx;
-       bool bidi = test_type == TEST_TYPE_BIDI;
-       bool bpf = test_type == TEST_TYPE_BPF_RES;
-       struct pkt_stream *pkt_stream;
+       pthread_t t0, t1;
 
        if (pthread_barrier_init(&barr, NULL, 2))
                exit_with_error(errno);
 
-       if (stat_test_type == STAT_TEST_TX_INVALID)
-               pkt_stream = pkt_stream_generate(test->ifobj_tx->umem, DEFAULT_PKT_CNT,
-                                                XSK_UMEM__INVALID_FRAME_SIZE);
-       else
-               pkt_stream = pkt_stream_generate(test->ifobj_tx->umem, DEFAULT_PKT_CNT, PKT_SIZE);
-       ifobj_tx->pkt_stream = pkt_stream;
-       ifobj_rx->pkt_stream = pkt_stream;
+       test->current_step++;
+       pkt_stream_reset(ifobj_rx->pkt_stream);
 
        /*Spawn RX thread */
-       pthread_create(&t0, NULL, ifobj_rx->func_ptr, ifobj_rx);
+       pthread_create(&t0, NULL, ifobj_rx->func_ptr, test);
 
        pthread_barrier_wait(&barr);
        if (pthread_barrier_destroy(&barr))
                exit_with_error(errno);
 
        /*Spawn TX thread */
-       pthread_create(&t1, NULL, ifobj_tx->func_ptr, ifobj_tx);
+       pthread_create(&t1, NULL, ifobj_tx->func_ptr, test);
 
        pthread_join(t1, NULL);
        pthread_join(t0, NULL);
-
-       if (!(test_type == TEST_TYPE_TEARDOWN) && !bidi && !bpf && !(test_type == TEST_TYPE_STATS))
-               print_ksft_result();
 }
 
 static void testapp_teardown(struct test_spec *test)
 {
        int i;
 
+       test_spec_set_name(test, "TEARDOWN");
        for (i = 0; i < MAX_TEARDOWN_ITER; i++) {
                testapp_validate_traffic(test);
                test_spec_reset(test);
        }
-
-       print_ksft_result();
 }
 
 static void swap_directions(struct ifobject **ifobj1, struct ifobject **ifobj2)
 {
        thread_func_t tmp_func_ptr = (*ifobj1)->func_ptr;
-       enum fvector tmp_vector = (*ifobj1)->fv.vector;
        struct ifobject *tmp_ifobj = (*ifobj1);
 
        (*ifobj1)->func_ptr = (*ifobj2)->func_ptr;
-       (*ifobj1)->fv.vector = (*ifobj2)->fv.vector;
-
        (*ifobj2)->func_ptr = tmp_func_ptr;
-       (*ifobj2)->fv.vector = tmp_vector;
 
        *ifobj1 = *ifobj2;
        *ifobj2 = tmp_ifobj;
@@ -942,19 +1084,17 @@ static void swap_directions(struct ifobject **ifobj1, struct ifobject **ifobj2)
 
 static void testapp_bidi(struct test_spec *test)
 {
-       for (int i = 0; i < MAX_BIDI_ITER; i++) {
-               print_verbose("Creating socket\n");
-               testapp_validate_traffic(test);
-               if (!second_step) {
-                       print_verbose("Switching Tx/Rx vectors\n");
-                       swap_directions(&test->ifobj_rx, &test->ifobj_tx);
-               }
-               second_step = true;
-       }
+       test_spec_set_name(test, "BIDIRECTIONAL");
+       test->ifobj_tx->rx_on = true;
+       test->ifobj_rx->tx_on = true;
+       test->total_steps = 2;
+       testapp_validate_traffic(test);
 
+       print_verbose("Switching Tx/Rx vectors\n");
        swap_directions(&test->ifobj_rx, &test->ifobj_tx);
+       testapp_validate_traffic(test);
 
-       print_ksft_result();
+       swap_directions(&test->ifobj_rx, &test->ifobj_tx);
 }
 
 static void swap_xsk_resources(struct ifobject *ifobj_tx, struct ifobject *ifobj_rx)
@@ -971,47 +1111,135 @@ static void swap_xsk_resources(struct ifobject *ifobj_tx, struct ifobject *ifobj
 
 static void testapp_bpf_res(struct test_spec *test)
 {
-       int i;
-
-       for (i = 0; i < MAX_BPF_ITER; i++) {
-               print_verbose("Creating socket\n");
-               testapp_validate_traffic(test);
-               if (!second_step)
-                       swap_xsk_resources(test->ifobj_tx, test->ifobj_rx);
-               second_step = true;
-       }
+       test_spec_set_name(test, "BPF_RES");
+       test->total_steps = 2;
+       test->nb_sockets = 2;
+       testapp_validate_traffic(test);
 
-       print_ksft_result();
+       swap_xsk_resources(test->ifobj_tx, test->ifobj_rx);
+       testapp_validate_traffic(test);
 }
 
 static void testapp_stats(struct test_spec *test)
 {
-       for (int i = 0; i < STAT_TEST_TYPE_MAX; i++) {
+       int i;
+
+       for (i = 0; i < STAT_TEST_TYPE_MAX; i++) {
                test_spec_reset(test);
                stat_test_type = i;
 
                switch (stat_test_type) {
                case STAT_TEST_RX_DROPPED:
+                       test_spec_set_name(test, "STAT_RX_DROPPED");
                        test->ifobj_rx->umem->frame_headroom = test->ifobj_rx->umem->frame_size -
                                XDP_PACKET_HEADROOM - 1;
+                       testapp_validate_traffic(test);
                        break;
                case STAT_TEST_RX_FULL:
+                       test_spec_set_name(test, "STAT_RX_FULL");
                        test->ifobj_rx->xsk->rxqsize = RX_FULL_RXQSIZE;
+                       testapp_validate_traffic(test);
                        break;
                case STAT_TEST_TX_INVALID:
-                       continue;
+                       test_spec_set_name(test, "STAT_TX_INVALID");
+                       pkt_stream_replace(test, DEFAULT_PKT_CNT, XSK_UMEM__INVALID_FRAME_SIZE);
+                       testapp_validate_traffic(test);
+
+                       pkt_stream_restore_default(test);
+                       break;
+               case STAT_TEST_RX_FILL_EMPTY:
+                       test_spec_set_name(test, "STAT_RX_FILL_EMPTY");
+                       test->ifobj_rx->pkt_stream = pkt_stream_generate(test->ifobj_rx->umem, 0,
+                                                                        MIN_PKT_SIZE);
+                       if (!test->ifobj_rx->pkt_stream)
+                               exit_with_error(ENOMEM);
+                       test->ifobj_rx->pkt_stream->use_addr_for_fill = true;
+                       testapp_validate_traffic(test);
+
+                       pkt_stream_restore_default(test);
+                       break;
                default:
                        break;
                }
-               testapp_validate_traffic(test);
        }
 
-       print_ksft_result();
+       /* To only see the whole stat set being completed unless an individual test fails. */
+       test_spec_set_name(test, "STATS");
+}
+
+/* Simple test */
+static bool hugepages_present(struct ifobject *ifobject)
+{
+       const size_t mmap_sz = 2 * ifobject->umem->num_frames * ifobject->umem->frame_size;
+       void *bufs;
+
+       bufs = mmap(NULL, mmap_sz, PROT_READ | PROT_WRITE,
+                   MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_HUGETLB, -1, 0);
+       if (bufs == MAP_FAILED)
+               return false;
+
+       munmap(bufs, mmap_sz);
+       return true;
+}
+
+static bool testapp_unaligned(struct test_spec *test)
+{
+       if (!hugepages_present(test->ifobj_tx)) {
+               ksft_test_result_skip("No 2M huge pages present.\n");
+               return false;
+       }
+
+       test_spec_set_name(test, "UNALIGNED_MODE");
+       test->ifobj_tx->umem->unaligned_mode = true;
+       test->ifobj_rx->umem->unaligned_mode = true;
+       /* Let half of the packets straddle a buffer boundrary */
+       pkt_stream_replace_half(test, PKT_SIZE, test->ifobj_tx->umem->frame_size - 32);
+       test->ifobj_rx->pkt_stream->use_addr_for_fill = true;
+       testapp_validate_traffic(test);
+
+       pkt_stream_restore_default(test);
+       return true;
+}
+
+static void testapp_invalid_desc(struct test_spec *test)
+{
+       struct pkt pkts[] = {
+               /* Zero packet length at address zero allowed */
+               {0, 0, 0, true},
+               /* Zero packet length allowed */
+               {0x1000, 0, 0, true},
+               /* Straddling the start of umem */
+               {-2, PKT_SIZE, 0, false},
+               /* Packet too large */
+               {0x2000, XSK_UMEM__INVALID_FRAME_SIZE, 0, false},
+               /* After umem ends */
+               {UMEM_SIZE, PKT_SIZE, 0, false},
+               /* Straddle the end of umem */
+               {UMEM_SIZE - PKT_SIZE / 2, PKT_SIZE, 0, false},
+               /* Straddle a page boundrary */
+               {0x3000 - PKT_SIZE / 2, PKT_SIZE, 0, false},
+               /* Straddle a 2K boundrary */
+               {0x3800 - PKT_SIZE / 2, PKT_SIZE, 0, true},
+               /* Valid packet for synch so that something is received */
+               {0x4000, PKT_SIZE, 0, true}};
+
+       if (test->ifobj_tx->umem->unaligned_mode) {
+               /* Crossing a page boundrary allowed */
+               pkts[6].valid = true;
+       }
+       if (test->ifobj_tx->umem->frame_size == XSK_UMEM__DEFAULT_FRAME_SIZE / 2) {
+               /* Crossing a 2K frame size boundrary not allowed */
+               pkts[7].valid = false;
+       }
+
+       pkt_stream_generate_custom(test, pkts, ARRAY_SIZE(pkts));
+       testapp_validate_traffic(test);
+       pkt_stream_restore_default(test);
 }
 
 static void init_iface(struct ifobject *ifobj, const char *dst_mac, const char *src_mac,
                       const char *dst_ip, const char *src_ip, const u16 dst_port,
-                      const u16 src_port, enum fvector vector, thread_func_t func_ptr)
+                      const u16 src_port, thread_func_t func_ptr)
 {
        struct in_addr ip;
 
@@ -1027,32 +1255,16 @@ static void init_iface(struct ifobject *ifobj, const char *dst_mac, const char *
        ifobj->dst_port = dst_port;
        ifobj->src_port = src_port;
 
-       ifobj->fv.vector = vector;
        ifobj->func_ptr = func_ptr;
 }
 
-static void run_pkt_test(struct test_spec *test, int mode, int type)
+static void run_pkt_test(struct test_spec *test, enum test_mode mode, enum test_type type)
 {
        test_type = type;
 
        /* reset defaults after potential previous test */
-       xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
-       second_step = 0;
        stat_test_type = -1;
 
-       configured_mode = mode;
-
-       switch (mode) {
-       case (TEST_MODE_SKB):
-               xdp_flags |= XDP_FLAGS_SKB_MODE;
-               break;
-       case (TEST_MODE_DRV):
-               xdp_flags |= XDP_FLAGS_DRV_MODE;
-               break;
-       default:
-               break;
-       }
-
        switch (test_type) {
        case TEST_TYPE_STATS:
                testapp_stats(test);
@@ -1066,10 +1278,50 @@ static void run_pkt_test(struct test_spec *test, int mode, int type)
        case TEST_TYPE_BPF_RES:
                testapp_bpf_res(test);
                break;
-       default:
+       case TEST_TYPE_RUN_TO_COMPLETION:
+               test_spec_set_name(test, "RUN_TO_COMPLETION");
                testapp_validate_traffic(test);
                break;
+       case TEST_TYPE_RUN_TO_COMPLETION_2K_FRAME:
+               test_spec_set_name(test, "RUN_TO_COMPLETION_2K_FRAME_SIZE");
+               test->ifobj_tx->umem->frame_size = 2048;
+               test->ifobj_rx->umem->frame_size = 2048;
+               pkt_stream_replace(test, DEFAULT_PKT_CNT, MIN_PKT_SIZE);
+               testapp_validate_traffic(test);
+
+               pkt_stream_restore_default(test);
+               break;
+       case TEST_TYPE_POLL:
+               test->ifobj_tx->use_poll = true;
+               test->ifobj_rx->use_poll = true;
+               test_spec_set_name(test, "POLL");
+               testapp_validate_traffic(test);
+               break;
+       case TEST_TYPE_ALIGNED_INV_DESC:
+               test_spec_set_name(test, "ALIGNED_INV_DESC");
+               testapp_invalid_desc(test);
+               break;
+       case TEST_TYPE_ALIGNED_INV_DESC_2K_FRAME:
+               test_spec_set_name(test, "ALIGNED_INV_DESC_2K_FRAME_SIZE");
+               test->ifobj_tx->umem->frame_size = 2048;
+               test->ifobj_rx->umem->frame_size = 2048;
+               testapp_invalid_desc(test);
+               break;
+       case TEST_TYPE_UNALIGNED_INV_DESC:
+               test_spec_set_name(test, "UNALIGNED_INV_DESC");
+               test->ifobj_tx->umem->unaligned_mode = true;
+               test->ifobj_rx->umem->unaligned_mode = true;
+               testapp_invalid_desc(test);
+               break;
+       case TEST_TYPE_UNALIGNED:
+               if (!testapp_unaligned(test))
+                       return;
+               break;
+       default:
+               break;
        }
+
+       print_ksft_result(test);
 }
 
 static struct ifobject *ifobject_create(void)
@@ -1107,6 +1359,7 @@ static void ifobject_delete(struct ifobject *ifobj)
 int main(int argc, char **argv)
 {
        struct rlimit _rlim = { RLIM_INFINITY, RLIM_INFINITY };
+       struct pkt_stream *pkt_stream_default;
        struct ifobject *ifobj_tx, *ifobj_rx;
        struct test_spec test;
        u32 i, j;
@@ -1121,31 +1374,36 @@ int main(int argc, char **argv)
        if (!ifobj_rx)
                exit_with_error(ENOMEM);
 
-       test_spec_init(&test, ifobj_tx, ifobj_rx);
-
        setlocale(LC_ALL, "");
 
-       parse_command_line(&test, argc, argv);
+       parse_command_line(ifobj_tx, ifobj_rx, argc, argv);
 
        if (!validate_interface(ifobj_tx) || !validate_interface(ifobj_rx)) {
                usage(basename(argv[0]));
                ksft_exit_xfail();
        }
 
-       init_iface(ifobj_tx, MAC1, MAC2, IP1, IP2, UDP_PORT1, UDP_PORT2, tx,
+       init_iface(ifobj_tx, MAC1, MAC2, IP1, IP2, UDP_PORT1, UDP_PORT2,
                   worker_testapp_validate_tx);
-       init_iface(ifobj_rx, MAC2, MAC1, IP2, IP1, UDP_PORT2, UDP_PORT1, rx,
+       init_iface(ifobj_rx, MAC2, MAC1, IP2, IP1, UDP_PORT2, UDP_PORT1,
                   worker_testapp_validate_rx);
 
+       test_spec_init(&test, ifobj_tx, ifobj_rx, 0);
+       pkt_stream_default = pkt_stream_generate(ifobj_tx->umem, DEFAULT_PKT_CNT, PKT_SIZE);
+       if (!pkt_stream_default)
+               exit_with_error(ENOMEM);
+       test.pkt_stream_default = pkt_stream_default;
+
        ksft_set_plan(TEST_MODE_MAX * TEST_TYPE_MAX);
 
        for (i = 0; i < TEST_MODE_MAX; i++)
                for (j = 0; j < TEST_TYPE_MAX; j++) {
-                       test_spec_init(&test, ifobj_tx, ifobj_rx);
+                       test_spec_init(&test, ifobj_tx, ifobj_rx, i);
                        run_pkt_test(&test, i, j);
                        usleep(USLEEP_MAX);
                }
 
+       pkt_stream_delete(pkt_stream_default);
        ifobject_delete(ifobj_tx);
        ifobject_delete(ifobj_rx);