Commit 89baa22d authored by David Wei's avatar David Wei Committed by Jens Axboe
Browse files

io_uring/zcrx: add selftest case for recvzc with read limit



Add a selftest case to iou-zcrx where the sender sends 4x4K = 16K and
the receiver does 4x4K recvzc requests. Validate that the requests
complete successfully and that the data is not corrupted.

Signed-off-by: default avatarDavid Wei <dw@davidwei.uk>
Link: https://lore.kernel.org/r/20250224041319.2389785-3-dw@davidwei.uk


Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent 6699ec9a
Loading
Loading
Loading
Loading
+37 −6
Original line number Diff line number Diff line
@@ -61,6 +61,9 @@ static int cfg_port = 8000;
static int cfg_payload_len;
static const char *cfg_ifname;
static int cfg_queue_id = -1;
static bool cfg_oneshot;
static int cfg_oneshot_recvs;
static int cfg_send_size = SEND_SIZE;
static struct sockaddr_in6 cfg_addr;

static char payload[SEND_SIZE] __attribute__((aligned(PAGE_SIZE)));
@@ -196,6 +199,17 @@ static void add_recvzc(struct io_uring *ring, int sockfd)
	sqe->user_data = 2;
}

static void add_recvzc_oneshot(struct io_uring *ring, int sockfd, size_t len)
{
	struct io_uring_sqe *sqe;

	sqe = io_uring_get_sqe(ring);

	io_uring_prep_rw(IORING_OP_RECV_ZC, sqe, sockfd, NULL, len, 0);
	sqe->ioprio |= IORING_RECV_MULTISHOT;
	sqe->user_data = 2;
}

static void process_accept(struct io_uring *ring, struct io_uring_cqe *cqe)
{
	if (cqe->res < 0)
@@ -204,6 +218,9 @@ static void process_accept(struct io_uring *ring, struct io_uring_cqe *cqe)
		error(1, 0, "Unexpected second connection");

	connfd = cqe->res;
	if (cfg_oneshot)
		add_recvzc_oneshot(ring, connfd, PAGE_SIZE);
	else
		add_recvzc(ring, connfd);
}

@@ -218,7 +235,7 @@ static void process_recvzc(struct io_uring *ring, struct io_uring_cqe *cqe)
	ssize_t n;
	int i;

	if (cqe->res == 0 && cqe->flags == 0) {
	if (cqe->res == 0 && cqe->flags == 0 && cfg_oneshot_recvs == 0) {
		stop = true;
		return;
	}
@@ -226,8 +243,14 @@ static void process_recvzc(struct io_uring *ring, struct io_uring_cqe *cqe)
	if (cqe->res < 0)
		error(1, 0, "recvzc(): %d", cqe->res);

	if (!(cqe->flags & IORING_CQE_F_MORE))
	if (cfg_oneshot) {
		if (cqe->res == 0 && cqe->flags == 0 && cfg_oneshot_recvs) {
			add_recvzc_oneshot(ring, connfd, PAGE_SIZE);
			cfg_oneshot_recvs--;
		}
	} else if (!(cqe->flags & IORING_CQE_F_MORE)) {
		add_recvzc(ring, connfd);
	}

	rcqe = (struct io_uring_zcrx_cqe *)(cqe + 1);

@@ -237,7 +260,7 @@ static void process_recvzc(struct io_uring *ring, struct io_uring_cqe *cqe)

	for (i = 0; i < n; i++) {
		if (*(data + i) != payload[(received + i)])
			error(1, 0, "payload mismatch");
			error(1, 0, "payload mismatch at ", i);
	}
	received += n;

@@ -313,7 +336,7 @@ static void run_server(void)

static void run_client(void)
{
	ssize_t to_send = SEND_SIZE;
	ssize_t to_send = cfg_send_size;
	ssize_t sent = 0;
	ssize_t chunk, res;
	int fd;
@@ -360,7 +383,7 @@ static void parse_opts(int argc, char **argv)
		usage(argv[0]);
	cfg_payload_len = max_payload_len;

	while ((c = getopt(argc, argv, "46sch:p:l:i:q:")) != -1) {
	while ((c = getopt(argc, argv, "sch:p:l:i:q:o:z:")) != -1) {
		switch (c) {
		case 's':
			if (cfg_client)
@@ -387,6 +410,14 @@ static void parse_opts(int argc, char **argv)
		case 'q':
			cfg_queue_id = strtoul(optarg, NULL, 0);
			break;
		case 'o': {
			cfg_oneshot = true;
			cfg_oneshot_recvs = strtoul(optarg, NULL, 0);
			break;
		}
		case 'z':
			cfg_send_size = strtoul(optarg, NULL, 0);
			break;
		}
	}

+25 −2
Original line number Diff line number Diff line
@@ -34,14 +34,37 @@ def test_zcrx(cfg) -> None:
        raise KsftSkipEx('at least 2 combined channels required')
    rx_ring = _get_rx_ring_entries(cfg)

    try:
        ethtool(f"-G {cfg.ifname} rx 64", host=cfg.remote)
        ethtool(f"-X {cfg.ifname} equal {combined_chans - 1}", host=cfg.remote)
        flow_rule_id = _set_flow_rule(cfg, combined_chans - 1)

        rx_cmd = f"{cfg.bin_remote} -s -p 9999 -i {cfg.ifname} -q {combined_chans - 1}"
        tx_cmd = f"{cfg.bin_local} -c -h {cfg.remote_v6} -p 9999 -l 12840"
        with bkg(rx_cmd, host=cfg.remote, exit_wait=True):
            wait_port_listen(9999, proto="tcp", host=cfg.remote)
            cmd(tx_cmd)
    finally:
        ethtool(f"-N {cfg.ifname} delete {flow_rule_id}", host=cfg.remote)
        ethtool(f"-X {cfg.ifname} default", host=cfg.remote)
        ethtool(f"-G {cfg.ifname} rx {rx_ring}", host=cfg.remote)


def test_zcrx_oneshot(cfg) -> None:
    cfg.require_v6()

    combined_chans = _get_combined_channels(cfg)
    if combined_chans < 2:
        raise KsftSkipEx('at least 2 combined channels required')
    rx_ring = _get_rx_ring_entries(cfg)

    try:
        ethtool(f"-G {cfg.ifname} rx 64", host=cfg.remote)
        ethtool(f"-X {cfg.ifname} equal {combined_chans - 1}", host=cfg.remote)
        flow_rule_id = _set_flow_rule(cfg, combined_chans - 1)

        rx_cmd = f"{cfg.bin_remote} -s -p 9999 -i {cfg.ifname} -q {combined_chans - 1} -o 4"
        tx_cmd = f"{cfg.bin_local} -c -h {cfg.remote_v6} -p 9999 -l 4096 -z 16384"
        with bkg(rx_cmd, host=cfg.remote, exit_wait=True):
            wait_port_listen(9999, proto="tcp", host=cfg.remote)
            cmd(tx_cmd)