Message ID | 20210901104732.10956-18-magnus.karlsson@gmail.com (mailing list archive) |
---|---|
State | Superseded |
Delegated to: | BPF |
Headers | show |
Series | selftests: xsk: facilitate adding tests | expand |
On Wed, Sep 01, 2021 at 12:47:29PM +0200, Magnus Karlsson wrote: > From: Magnus Karlsson <magnus.karlsson@intel.com> > > Add a test for unaligned mode in which packet buffers can be placed > anywhere within the umem. Some packets are made to straddle page > boundraries in order to check for correctness. On the Tx side, buffers boundaries > are now allocated according to the addresses found in the packet > stream. Thus, the placement of buffers can be controlled with the > boolean use_addr_for_fill in the packet stream. > > One new pkt_stream insterface is introduced: pkt_stream_replace_half() interface > that replaces every other packet in the default packet stream with the > specified new packet. Can you describe the introduction of DEFAULT_OFFSET ? > > Signed-off-by: Magnus Karlsson <magnus.karlsson@intel.com> > --- > tools/testing/selftests/bpf/xdpxceiver.c | 125 ++++++++++++++++++----- > tools/testing/selftests/bpf/xdpxceiver.h | 4 + > 2 files changed, 106 insertions(+), 23 deletions(-) > > diff --git a/tools/testing/selftests/bpf/xdpxceiver.c b/tools/testing/selftests/bpf/xdpxceiver.c > index d4aad4833754..a24068993cc3 100644 > --- a/tools/testing/selftests/bpf/xdpxceiver.c > +++ b/tools/testing/selftests/bpf/xdpxceiver.c > @@ -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 > @@ -45,6 +45,7 @@ > * 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 > * > * Total tests: 12 > * > @@ -243,6 +244,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) > @@ -252,19 +256,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) > { > @@ -487,7 +478,8 @@ static struct pkt_stream *pkt_stream_generate(struct xsk_umem_info *umem, u32 nb > > 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; Probably we need to init use_addr_for_fill to false by default in here as pkt_stream is malloc'd. > > @@ -500,6 +492,12 @@ static struct pkt_stream *pkt_stream_generate(struct xsk_umem_info *umem, u32 nb > 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; > @@ -507,8 +505,22 @@ static void pkt_stream_replace(struct test_spec *test, u32 nb_pkts, u32 pkt_len) > 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; > +} > > - pkt_stream_delete(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) > @@ -572,9 +584,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) { > @@ -588,10 +600,10 @@ static bool is_pkt_valid(struct pkt *pkt, void *buffer, const struct xdp_desc *d > if (opt_pkt_dump) > pkt_dump(data, PKT_SIZE); > > - if (pkt->len != desc->len) { > + if (pkt->len != len) { > ksft_test_result_fail > ("ERROR: [%s] expected length [%d], got length [%d]\n", > - __func__, pkt->len, desc->len); > + __func__, pkt->len, len); > return false; > } > > @@ -673,7 +685,7 @@ static void receive_pkts(struct pkt_stream *pkt_stream, struct xsk_socket_info * > > 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; > @@ -817,13 +829,16 @@ static void tx_stats_validate(struct ifobject *ifobject) > > static void thread_common_ops(struct test_spec *test, struct ifobject *ifobject) > { > + int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE; > u32 i; > > ifobject->ns_fd = switch_namespace(ifobject->nsname); > > + if (ifobject->umem->unaligned_mode) > + mmap_flags |= MAP_HUGETLB; > + > for (i = 0; i < test->nb_sockets; i++) { > u64 umem_sz = ifobject->umem->num_frames * ifobject->umem->frame_size; > - int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE; > u32 ctr = 0; > void *bufs; > > @@ -881,6 +896,32 @@ static void *worker_testapp_validate_tx(void *arg) > pthread_exit(NULL); > } > > +static void xsk_populate_fill_ring(struct xsk_umem_info *umem, struct pkt_stream *pkt_stream) > +{ > + u32 idx = 0, i; > + int ret; > + > + 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(ENOSPC); -ENOSPC? > + for (i = 0; i < XSK_RING_PROD__DEFAULT_NUM_DESCS; 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->num_frames) * umem->frame_size + DEFAULT_OFFSET; > + } > + > + *xsk_ring_prod__fill_addr(&umem->fq, idx++) = addr; > + } > + xsk_ring_prod__submit(&umem->fq, XSK_RING_PROD__DEFAULT_NUM_DESCS); > +}
On Fri, Sep 3, 2021 at 3:37 PM Maciej Fijalkowski <maciej.fijalkowski@intel.com> wrote: > > On Wed, Sep 01, 2021 at 12:47:29PM +0200, Magnus Karlsson wrote: > > From: Magnus Karlsson <magnus.karlsson@intel.com> > > > > Add a test for unaligned mode in which packet buffers can be placed > > anywhere within the umem. Some packets are made to straddle page > > boundraries in order to check for correctness. On the Tx side, buffers > > boundaries > > > are now allocated according to the addresses found in the packet > > stream. Thus, the placement of buffers can be controlled with the > > boolean use_addr_for_fill in the packet stream. > > > > One new pkt_stream insterface is introduced: pkt_stream_replace_half() > > interface > > > that replaces every other packet in the default packet stream with the > > specified new packet. > > Can you describe the introduction of DEFAULT_OFFSET ? Will fix both. > > > > Signed-off-by: Magnus Karlsson <magnus.karlsson@intel.com> > > --- > > tools/testing/selftests/bpf/xdpxceiver.c | 125 ++++++++++++++++++----- > > tools/testing/selftests/bpf/xdpxceiver.h | 4 + > > 2 files changed, 106 insertions(+), 23 deletions(-) > > > > diff --git a/tools/testing/selftests/bpf/xdpxceiver.c b/tools/testing/selftests/bpf/xdpxceiver.c > > index d4aad4833754..a24068993cc3 100644 > > --- a/tools/testing/selftests/bpf/xdpxceiver.c > > +++ b/tools/testing/selftests/bpf/xdpxceiver.c > > @@ -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 > > @@ -45,6 +45,7 @@ > > * 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 > > * > > * Total tests: 12 > > * > > @@ -243,6 +244,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) > > @@ -252,19 +256,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) > > { > > @@ -487,7 +478,8 @@ static struct pkt_stream *pkt_stream_generate(struct xsk_umem_info *umem, u32 nb > > > > 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; > > Probably we need to init use_addr_for_fill to false by default in here as > pkt_stream is malloc'd. I will use calloc here, instead of in patch #19. Was fixed too late in the patch set. > > > > @@ -500,6 +492,12 @@ static struct pkt_stream *pkt_stream_generate(struct xsk_umem_info *umem, u32 nb > > 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; > > @@ -507,8 +505,22 @@ static void pkt_stream_replace(struct test_spec *test, u32 nb_pkts, u32 pkt_len) > > 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; > > +} > > > > - pkt_stream_delete(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) > > @@ -572,9 +584,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) { > > @@ -588,10 +600,10 @@ static bool is_pkt_valid(struct pkt *pkt, void *buffer, const struct xdp_desc *d > > if (opt_pkt_dump) > > pkt_dump(data, PKT_SIZE); > > > > - if (pkt->len != desc->len) { > > + if (pkt->len != len) { > > ksft_test_result_fail > > ("ERROR: [%s] expected length [%d], got length [%d]\n", > > - __func__, pkt->len, desc->len); > > + __func__, pkt->len, len); > > return false; > > } > > > > @@ -673,7 +685,7 @@ static void receive_pkts(struct pkt_stream *pkt_stream, struct xsk_socket_info * > > > > 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; > > @@ -817,13 +829,16 @@ static void tx_stats_validate(struct ifobject *ifobject) > > > > static void thread_common_ops(struct test_spec *test, struct ifobject *ifobject) > > { > > + int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE; > > u32 i; > > > > ifobject->ns_fd = switch_namespace(ifobject->nsname); > > > > + if (ifobject->umem->unaligned_mode) > > + mmap_flags |= MAP_HUGETLB; > > + > > for (i = 0; i < test->nb_sockets; i++) { > > u64 umem_sz = ifobject->umem->num_frames * ifobject->umem->frame_size; > > - int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE; > > u32 ctr = 0; > > void *bufs; > > > > @@ -881,6 +896,32 @@ static void *worker_testapp_validate_tx(void *arg) > > pthread_exit(NULL); > > } > > > > +static void xsk_populate_fill_ring(struct xsk_umem_info *umem, struct pkt_stream *pkt_stream) > > +{ > > + u32 idx = 0, i; > > + int ret; > > + > > + 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(ENOSPC); > > -ENOSPC? Without the minus sign is correct here. Though I would prefer to not use exit_with_error at all except for in the main function, this would require a lot of surgery and is left as an exercise for later patch sets. > > + for (i = 0; i < XSK_RING_PROD__DEFAULT_NUM_DESCS; 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->num_frames) * umem->frame_size + DEFAULT_OFFSET; > > + } > > + > > + *xsk_ring_prod__fill_addr(&umem->fq, idx++) = addr; > > + } > > + xsk_ring_prod__submit(&umem->fq, XSK_RING_PROD__DEFAULT_NUM_DESCS); > > +}
diff --git a/tools/testing/selftests/bpf/xdpxceiver.c b/tools/testing/selftests/bpf/xdpxceiver.c index d4aad4833754..a24068993cc3 100644 --- a/tools/testing/selftests/bpf/xdpxceiver.c +++ b/tools/testing/selftests/bpf/xdpxceiver.c @@ -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 @@ -45,6 +45,7 @@ * 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 * * Total tests: 12 * @@ -243,6 +244,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) @@ -252,19 +256,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) { @@ -487,7 +478,8 @@ static struct pkt_stream *pkt_stream_generate(struct xsk_umem_info *umem, u32 nb 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; @@ -500,6 +492,12 @@ static struct pkt_stream *pkt_stream_generate(struct xsk_umem_info *umem, u32 nb 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; @@ -507,8 +505,22 @@ static void pkt_stream_replace(struct test_spec *test, u32 nb_pkts, u32 pkt_len) 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; +} - pkt_stream_delete(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) @@ -572,9 +584,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) { @@ -588,10 +600,10 @@ static bool is_pkt_valid(struct pkt *pkt, void *buffer, const struct xdp_desc *d if (opt_pkt_dump) pkt_dump(data, PKT_SIZE); - if (pkt->len != desc->len) { + if (pkt->len != len) { ksft_test_result_fail ("ERROR: [%s] expected length [%d], got length [%d]\n", - __func__, pkt->len, desc->len); + __func__, pkt->len, len); return false; } @@ -673,7 +685,7 @@ static void receive_pkts(struct pkt_stream *pkt_stream, struct xsk_socket_info * 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; @@ -817,13 +829,16 @@ static void tx_stats_validate(struct ifobject *ifobject) static void thread_common_ops(struct test_spec *test, struct ifobject *ifobject) { + int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE; u32 i; ifobject->ns_fd = switch_namespace(ifobject->nsname); + if (ifobject->umem->unaligned_mode) + mmap_flags |= MAP_HUGETLB; + for (i = 0; i < test->nb_sockets; i++) { u64 umem_sz = ifobject->umem->num_frames * ifobject->umem->frame_size; - int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE; u32 ctr = 0; void *bufs; @@ -881,6 +896,32 @@ static void *worker_testapp_validate_tx(void *arg) pthread_exit(NULL); } +static void xsk_populate_fill_ring(struct xsk_umem_info *umem, struct pkt_stream *pkt_stream) +{ + u32 idx = 0, i; + int ret; + + 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(ENOSPC); + for (i = 0; i < XSK_RING_PROD__DEFAULT_NUM_DESCS; 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->num_frames) * umem->frame_size + DEFAULT_OFFSET; + } + + *xsk_ring_prod__fill_addr(&umem->fq, idx++) = addr; + } + xsk_ring_prod__submit(&umem->fq, XSK_RING_PROD__DEFAULT_NUM_DESCS); +} + static void *worker_testapp_validate_rx(void *arg) { struct test_spec *test = (struct test_spec *)arg; @@ -891,7 +932,7 @@ static void *worker_testapp_validate_rx(void *arg) 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.fd = xsk_socket__fd(ifobject->xsk->xsk); fds.events = POLLIN; @@ -1035,6 +1076,40 @@ static void testapp_stats(struct test_spec *test) 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 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, thread_func_t func_ptr) @@ -1086,6 +1161,10 @@ static void run_pkt_test(struct test_spec *test, enum test_mode mode, enum test_ test_spec_set_name(test, "POLL"); testapp_validate_traffic(test); break; + case TEST_TYPE_UNALIGNED: + if (!testapp_unaligned(test)) + return; + break; default: break; } diff --git a/tools/testing/selftests/bpf/xdpxceiver.h b/tools/testing/selftests/bpf/xdpxceiver.h index e27fe348ae50..129801eb013c 100644 --- a/tools/testing/selftests/bpf/xdpxceiver.h +++ b/tools/testing/selftests/bpf/xdpxceiver.h @@ -39,6 +39,7 @@ #define POLL_TMOUT 1000 #define DEFAULT_PKT_CNT (4 * 1024) #define RX_FULL_RXQSIZE 32 +#define DEFAULT_OFFSET 256 #define XSK_UMEM__INVALID_FRAME_SIZE (XSK_UMEM__DEFAULT_FRAME_SIZE + 1) #define print_verbose(x...) do { if (opt_verbose) ksft_print_msg(x); } while (0) @@ -52,6 +53,7 @@ enum test_mode { enum test_type { TEST_TYPE_NOPOLL, TEST_TYPE_POLL, + TEST_TYPE_UNALIGNED, TEST_TYPE_TEARDOWN, TEST_TYPE_BIDI, TEST_TYPE_STATS, @@ -81,6 +83,7 @@ struct xsk_umem_info { u32 frame_headroom; void *buffer; u32 frame_size; + bool unaligned_mode; }; struct xsk_socket_info { @@ -102,6 +105,7 @@ struct pkt { struct pkt_stream { u32 nb_pkts; struct pkt *pkts; + bool use_addr_for_fill; }; typedef void *(*thread_func_t)(void *arg);