mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 17:47:41 +02:00
Merge branch 'selftests-xsk-stabilize-timeout-test-behavior'
Tushar Vyavahare says: ==================== selftests/xsk: stabilize timeout test behavior This series improves AF_XDP selftests by making timeout handling explicit and fixing sources of non-determinism in xsk timeout tests. Patch 1 introduces test_spec::poll_tmout and removes implicit dependence on RX UMEM setup state for timeout behavior. Patch 2 fixes thread harness sequencing by attaching XDP programs before worker startup, removing signal-based termination, and using barrier synchronization only for dual-thread runs. Patch 3 restores shared_umem after POLL_TXQ_FULL so test-local configuration does not leak into subsequent cases on shared-netdev runs. Together these changes make timeout handling easier to follow and improve selftest stability, especially on real NIC runs. ==================== Link: https://patch.msgid.link/20260616154955.1492560-1-tushar.vyavahare@intel.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
0ae44b88da
|
|
@ -7,7 +7,6 @@
|
|||
#include <linux/netdev.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/socket.h>
|
||||
|
|
@ -65,11 +64,6 @@ static void gen_eth_hdr(struct xsk_socket_info *xsk, struct ethhdr *eth_hdr)
|
|||
eth_hdr->h_proto = htons(ETH_P_LOOPBACK);
|
||||
}
|
||||
|
||||
static bool is_umem_valid(struct xsk_socket_info *xsk)
|
||||
{
|
||||
return !!xsk->umem->umem;
|
||||
}
|
||||
|
||||
static u32 mode_to_xdp_flags(enum test_mode mode)
|
||||
{
|
||||
return (mode == TEST_MODE_SKB) ? XDP_FLAGS_SKB_MODE : XDP_FLAGS_DRV_MODE;
|
||||
|
|
@ -1010,7 +1004,7 @@ static int __receive_pkts(struct test_spec *test, struct xsk_socket_info *xsk)
|
|||
return TEST_FAILURE;
|
||||
|
||||
if (!ret) {
|
||||
if (!is_umem_valid(test->ifobj_tx->xsk))
|
||||
if (test->poll_tmout)
|
||||
return TEST_PASS;
|
||||
|
||||
ksft_print_msg("ERROR: [%s] Poll timed out\n", __func__);
|
||||
|
|
@ -1149,7 +1143,7 @@ static int receive_pkts(struct test_spec *test)
|
|||
break;
|
||||
|
||||
res = __receive_pkts(test, xsk);
|
||||
if (!(res == TEST_PASS || res == TEST_CONTINUE))
|
||||
if (res != TEST_CONTINUE)
|
||||
return res;
|
||||
|
||||
ret = gettimeofday(&tv_now, NULL);
|
||||
|
|
@ -1166,7 +1160,8 @@ static int receive_pkts(struct test_spec *test)
|
|||
return TEST_PASS;
|
||||
}
|
||||
|
||||
static int __send_pkts(struct ifobject *ifobject, struct xsk_socket_info *xsk, bool timeout)
|
||||
static int __send_pkts(struct ifobject *ifobject, struct xsk_socket_info *xsk,
|
||||
bool test_timeout)
|
||||
{
|
||||
u32 i, idx = 0, valid_pkts = 0, valid_frags = 0, buffer_len;
|
||||
struct pkt_stream *pkt_stream = xsk->pkt_stream;
|
||||
|
|
@ -1178,7 +1173,7 @@ static int __send_pkts(struct ifobject *ifobject, struct xsk_socket_info *xsk, b
|
|||
buffer_len = pkt_get_buffer_len(umem, pkt_stream->max_pkt_len);
|
||||
/* pkts_in_flight might be negative if many invalid packets are sent */
|
||||
if (pkts_in_flight >= (int)((umem_size(umem) - xsk->batch_size * buffer_len) /
|
||||
buffer_len)) {
|
||||
buffer_len) && !test_timeout) {
|
||||
ret = kick_tx(xsk);
|
||||
if (ret)
|
||||
return TEST_FAILURE;
|
||||
|
|
@ -1191,7 +1186,7 @@ static int __send_pkts(struct ifobject *ifobject, struct xsk_socket_info *xsk, b
|
|||
while (xsk_ring_prod__reserve(&xsk->tx, xsk->batch_size, &idx) < xsk->batch_size) {
|
||||
if (use_poll) {
|
||||
ret = poll(&fds, 1, POLL_TMOUT);
|
||||
if (timeout) {
|
||||
if (test_timeout) {
|
||||
if (ret < 0) {
|
||||
ksft_print_msg("ERROR: [%s] Poll error %d\n",
|
||||
__func__, errno);
|
||||
|
|
@ -1271,7 +1266,7 @@ static int __send_pkts(struct ifobject *ifobject, struct xsk_socket_info *xsk, b
|
|||
if (use_poll) {
|
||||
ret = poll(&fds, 1, POLL_TMOUT);
|
||||
if (ret <= 0) {
|
||||
if (ret == 0 && timeout)
|
||||
if (ret == 0 && test_timeout)
|
||||
return TEST_PASS;
|
||||
|
||||
ksft_print_msg("ERROR: [%s] Poll error %d\n", __func__, ret);
|
||||
|
|
@ -1279,14 +1274,14 @@ static int __send_pkts(struct ifobject *ifobject, struct xsk_socket_info *xsk, b
|
|||
}
|
||||
}
|
||||
|
||||
if (!timeout) {
|
||||
if (!test_timeout) {
|
||||
if (complete_pkts(xsk, i))
|
||||
return TEST_FAILURE;
|
||||
|
||||
usleep(10);
|
||||
return TEST_PASS;
|
||||
}
|
||||
|
||||
/* Loop completion is driven by send_pkts() stream progress checks. */
|
||||
return TEST_CONTINUE;
|
||||
}
|
||||
|
||||
|
|
@ -1322,7 +1317,6 @@ bool all_packets_sent(struct test_spec *test, unsigned long *bitmap)
|
|||
|
||||
static int send_pkts(struct test_spec *test, struct ifobject *ifobject)
|
||||
{
|
||||
bool timeout = !is_umem_valid(test->ifobj_rx->xsk);
|
||||
DECLARE_BITMAP(bitmap, test->nb_sockets);
|
||||
u32 i, ret;
|
||||
|
||||
|
|
@ -1337,19 +1331,18 @@ static int send_pkts(struct test_spec *test, struct ifobject *ifobject)
|
|||
__set_bit(i, bitmap);
|
||||
continue;
|
||||
}
|
||||
ret = __send_pkts(ifobject, &ifobject->xsk_arr[i], timeout);
|
||||
if (ret == TEST_CONTINUE && !test->fail)
|
||||
continue;
|
||||
|
||||
if ((ret || test->fail) && !timeout)
|
||||
return TEST_FAILURE;
|
||||
|
||||
if (ret == TEST_PASS && timeout)
|
||||
ret = __send_pkts(ifobject, &ifobject->xsk_arr[i], test->poll_tmout);
|
||||
if (ret != TEST_CONTINUE)
|
||||
return ret;
|
||||
|
||||
ret = wait_for_tx_completion(&ifobject->xsk_arr[i]);
|
||||
if (ret)
|
||||
if (test->fail)
|
||||
return TEST_FAILURE;
|
||||
|
||||
if (!test->poll_tmout) {
|
||||
ret = wait_for_tx_completion(&ifobject->xsk_arr[i]);
|
||||
if (ret)
|
||||
return TEST_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1677,7 +1670,8 @@ void *worker_testapp_validate_rx(void *arg)
|
|||
strerror(-err));
|
||||
}
|
||||
|
||||
pthread_barrier_wait(&barr);
|
||||
if (test->use_barrier)
|
||||
pthread_barrier_wait(&barr);
|
||||
|
||||
/* We leave only now in case of error to avoid getting stuck in the barrier */
|
||||
if (err) {
|
||||
|
|
@ -1716,11 +1710,6 @@ static void testapp_clean_xsk_umem(struct ifobject *ifobj)
|
|||
munmap(umem->buffer, umem->mmap_size);
|
||||
}
|
||||
|
||||
static void handler(int signum)
|
||||
{
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
static bool xdp_prog_changed_rx(struct test_spec *test)
|
||||
{
|
||||
struct ifobject *ifobj = test->ifobj_rx;
|
||||
|
|
@ -1825,9 +1814,18 @@ static int __testapp_validate_traffic(struct test_spec *test, struct ifobject *i
|
|||
return TEST_FAILURE;
|
||||
}
|
||||
|
||||
if (ifobj2) {
|
||||
err = xsk_attach_xdp_progs(test, ifobj1, ifobj2);
|
||||
if (err) {
|
||||
ksft_print_msg("Error: failed to attach XDP programs: %d (%s)\n",
|
||||
err, strerror(-err));
|
||||
return TEST_FAILURE;
|
||||
}
|
||||
test->use_barrier = !!ifobj2;
|
||||
|
||||
if (test->use_barrier) {
|
||||
if (pthread_barrier_init(&barr, NULL, 2))
|
||||
return TEST_FAILURE;
|
||||
|
||||
pkt_stream_reset(ifobj2->xsk->pkt_stream);
|
||||
}
|
||||
|
||||
|
|
@ -1835,27 +1833,26 @@ static int __testapp_validate_traffic(struct test_spec *test, struct ifobject *i
|
|||
pkt_stream_reset(ifobj1->xsk->pkt_stream);
|
||||
pkts_in_flight = 0;
|
||||
|
||||
signal(SIGUSR1, handler);
|
||||
/*Spawn RX thread */
|
||||
pthread_create(&t0, NULL, ifobj1->func_ptr, test);
|
||||
|
||||
if (ifobj2) {
|
||||
if (test->use_barrier) {
|
||||
pthread_barrier_wait(&barr);
|
||||
if (pthread_barrier_destroy(&barr)) {
|
||||
pthread_kill(t0, SIGUSR1);
|
||||
test->use_barrier = false;
|
||||
pthread_join(t0, NULL);
|
||||
clean_sockets(test, ifobj1);
|
||||
clean_umem(test, ifobj1, NULL);
|
||||
return TEST_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (ifobj2) {
|
||||
/*Spawn TX thread */
|
||||
pthread_create(&t1, NULL, ifobj2->func_ptr, test);
|
||||
|
||||
pthread_join(t1, NULL);
|
||||
}
|
||||
|
||||
if (!ifobj2)
|
||||
pthread_kill(t0, SIGUSR1);
|
||||
pthread_join(t0, NULL);
|
||||
|
||||
if (test->total_steps == test->current_step || test->fail) {
|
||||
|
|
@ -1893,8 +1890,6 @@ static int testapp_validate_traffic(struct test_spec *test)
|
|||
}
|
||||
}
|
||||
|
||||
if (xsk_attach_xdp_progs(test, ifobj_rx, ifobj_tx))
|
||||
return TEST_FAILURE;
|
||||
return __testapp_validate_traffic(test, ifobj_rx, ifobj_tx);
|
||||
}
|
||||
|
||||
|
|
@ -2231,16 +2226,33 @@ int testapp_xdp_shared_umem(struct test_spec *test)
|
|||
|
||||
int testapp_poll_txq_tmout(struct test_spec *test)
|
||||
{
|
||||
bool shared_umem = test->ifobj_tx->shared_umem;
|
||||
int ret;
|
||||
|
||||
test->poll_tmout = true;
|
||||
/*
|
||||
* POLL_TXQ_FULL exercises TX timeout setup in isolation.
|
||||
* Keep TX out of shared-UMEM mode here so TX setup does not require
|
||||
* RX UMEM to be initialized first.
|
||||
*/
|
||||
test->ifobj_tx->shared_umem = false;
|
||||
test->ifobj_tx->use_poll = true;
|
||||
/* create invalid frame by set umem frame_size and pkt length equal to 2048 */
|
||||
test->ifobj_tx->xsk->umem->frame_size = 2048;
|
||||
if (pkt_stream_replace(test, 2 * DEFAULT_PKT_CNT, 2048))
|
||||
if (pkt_stream_replace(test, 2 * DEFAULT_PKT_CNT, 2048)) {
|
||||
test->ifobj_tx->shared_umem = shared_umem;
|
||||
return TEST_FAILURE;
|
||||
return testapp_validate_traffic_single_thread(test, test->ifobj_tx);
|
||||
}
|
||||
|
||||
ret = testapp_validate_traffic_single_thread(test, test->ifobj_tx);
|
||||
test->ifobj_tx->shared_umem = shared_umem;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int testapp_poll_rxq_tmout(struct test_spec *test)
|
||||
{
|
||||
test->poll_tmout = true;
|
||||
test->ifobj_rx->use_poll = true;
|
||||
return testapp_validate_traffic_single_thread(test, test->ifobj_rx);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -207,6 +207,8 @@ struct test_spec {
|
|||
bool set_ring;
|
||||
bool adjust_tail;
|
||||
bool adjust_tail_support;
|
||||
bool poll_tmout;
|
||||
bool use_barrier;
|
||||
enum test_mode mode;
|
||||
char name[MAX_TEST_NAME_SIZE];
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user