#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <pthread.h>
#include <net/if.h>
#include "liburing.h"
#include "helpers.h"
static unsigned int ifidx, rxq;
static long page_size;
#define AREA_PAGES 132
#define AREA_SZ (AREA_PAGES * page_size)
#define RQ_ENTRIES 128
#define LOOP_COUNT 5
#define DEV_ENV_VAR "NETIF"
#define RXQ_ENV_VAR "NETRXQ"
#define RING_FLAGS (IORING_SETUP_DEFER_TASKRUN | \
IORING_SETUP_CQE32 | \
IORING_SETUP_SINGLE_ISSUER)
static char str[] = "iv5t4dl500w7wsrf14fsuq8thptto0z7i2q62z1p8dwrv5u4kaxpqhm2rb7bapddi5gfkh7f9695eh46t2o5yap2y43gstbsq3n90bg1i7zx1m4wojoqbuxhsrw4s4y3sh9qp57ovbaa2o9yaqa7d4to2vak1otvgkoxs5t0ovjbe6roginrjeh906kmjn1289jlho9a1bud02ex4xr3cvfcybpl6axnr117p0aesb3070wlvj91en7tpf8nyb1e";
#define MSG_SIZE 512
#define PORT 10202
#define HOST "127.0.0.1"
static int probe_zcrx(void *area)
{
struct io_uring_zcrx_area_reg area_reg = {
.addr = uring_ptr_to_u64(area),
.len = AREA_SZ,
.flags = 0,
};
struct io_uring_zcrx_ifq_reg reg = {
.if_idx = ifidx,
.if_rxq = rxq,
.rq_entries = RQ_ENTRIES,
.area_ptr = uring_ptr_to_u64(&area_reg),
};
struct io_uring ring;
int ret;
ret = t_create_ring(8, &ring, RING_FLAGS);
if (ret == T_SETUP_SKIP) {
fprintf(stderr, "required ring flags are not supported, skip\n");
return T_EXIT_SKIP;
}
if (ret) {
fprintf(stderr, "probe ring setup failure\n");
return T_EXIT_FAIL;
}
ret = io_uring_register_ifq(&ring, ®);
if (ret == -EINVAL) {
fprintf(stderr, "zcrx is not supported, skip\n");
return T_EXIT_SKIP;
}
if (ret) {
fprintf(stderr, "probe zcrx register fail %i\n", ret);
return T_EXIT_FAIL;
}
io_uring_queue_exit(&ring);
return T_EXIT_PASS;
}
static int try_register_ifq(struct io_uring_zcrx_ifq_reg *reg)
{
struct io_uring ring;
int ret;
ret = t_create_ring(8, &ring, RING_FLAGS);
if (ret != T_SETUP_OK) {
fprintf(stderr, "ring create failed: %d\n", ret);
exit(T_EXIT_FAIL);
}
ret = io_uring_register_ifq(&ring, reg);
io_uring_queue_exit(&ring);
return ret;
}
static int test_invalid_if(void *area)
{
int ret;
struct io_uring_zcrx_area_reg area_reg = {
.addr = uring_ptr_to_u64(area),
.len = AREA_SZ,
.flags = 0,
};
struct io_uring_zcrx_ifq_reg reg = {
.if_idx = -1,
.if_rxq = rxq,
.rq_entries = RQ_ENTRIES,
.area_ptr = uring_ptr_to_u64(&area_reg),
};
ret = try_register_ifq(®);
if (ret != -EINVAL && ret != -ENODEV) {
fprintf(stderr, "registered invalid IF %i\n", ret);
return T_EXIT_FAIL;
}
reg.if_idx = ifidx;
reg.if_rxq = -1;
ret = try_register_ifq(®);
if (ret != -EINVAL) {
fprintf(stderr, "registered invalid IFQ %i\n", ret);
return T_EXIT_FAIL;
}
return T_EXIT_PASS;
}
static int test_invalid_ifq_collision(void *area)
{
struct io_uring ring, ring2;
struct io_uring_zcrx_area_reg area_reg = {
.addr = uring_ptr_to_u64(area),
.len = AREA_SZ,
.flags = 0,
};
struct io_uring_zcrx_ifq_reg reg = {
.if_idx = ifidx,
.if_rxq = rxq,
.rq_entries = RQ_ENTRIES,
.area_ptr = uring_ptr_to_u64(&area_reg),
};
int ret;
ret = t_create_ring(8, &ring, RING_FLAGS);
if (ret != T_SETUP_OK) {
fprintf(stderr, "ring create failed: %d\n", ret);
return T_EXIT_FAIL;
}
ret = t_create_ring(8, &ring2, RING_FLAGS);
if (ret != T_SETUP_OK) {
fprintf(stderr, "ring2 create failed: %d\n", ret);
return T_EXIT_FAIL;
}
ret = io_uring_register_ifq(&ring, ®);
if (ret) {
fprintf(stderr, "initial registration failed %i\n", ret);
return T_EXIT_FAIL;
}
ret = io_uring_register_ifq(&ring, ®);
if (!ret) {
fprintf(stderr, "registered taken queue\n");
return T_EXIT_FAIL;
}
ret = io_uring_register_ifq(&ring2, ®);
if (!ret) {
fprintf(stderr, "registered taken queue ring2\n");
return T_EXIT_FAIL;
}
io_uring_queue_exit(&ring);
io_uring_queue_exit(&ring2);
return T_EXIT_PASS;
}
static int test_rq_setup(void *area)
{
int ret;
struct io_uring_zcrx_area_reg area_reg = {
.addr = uring_ptr_to_u64(area),
.len = AREA_SZ,
.flags = 0,
};
struct io_uring_zcrx_ifq_reg reg = {
.if_idx = ifidx,
.if_rxq = rxq,
.rq_entries = 0,
.area_ptr = uring_ptr_to_u64(&area_reg),
};
ret = try_register_ifq(®);
if (ret != -EINVAL) {
fprintf(stderr, "registered 0 rq entries\n");
return T_EXIT_FAIL;
}
reg.rq_entries = (__u32)-1;
ret = try_register_ifq(®);
if (ret != -EINVAL) {
fprintf(stderr, "registered unlimited nr of rq entries\n");
return T_EXIT_FAIL;
}
reg.rq_entries = RQ_ENTRIES - 1;
ret = try_register_ifq(®);
if (ret != 0) {
fprintf(stderr, "ifq registration failed %i\n", ret);
return T_EXIT_FAIL;
}
if (reg.rq_entries == RQ_ENTRIES - 1) {
fprintf(stderr, "registered non pow2 refill entries %i\n",
reg.rq_entries);
return T_EXIT_FAIL;
}
return T_EXIT_PASS;
}
static int test_null_area_reg_struct(void)
{
int ret;
struct io_uring_zcrx_ifq_reg reg = {
.if_idx = ifidx,
.if_rxq = rxq,
.rq_entries = RQ_ENTRIES,
.area_ptr = uring_ptr_to_u64(0),
};
ret = try_register_ifq(®);
return ret ? T_EXIT_PASS : T_EXIT_FAIL;
}
static int test_null_area(void)
{
int ret;
struct io_uring_zcrx_area_reg area_reg = {
.addr = uring_ptr_to_u64(0),
.len = AREA_SZ,
.flags = 0,
};
struct io_uring_zcrx_ifq_reg reg = {
.if_idx = ifidx,
.if_rxq = rxq,
.rq_entries = RQ_ENTRIES,
.area_ptr = uring_ptr_to_u64(&area_reg),
};
ret = try_register_ifq(®);
return ret ? T_EXIT_PASS : T_EXIT_FAIL;
}
static int test_misaligned_area(void *area)
{
int ret;
struct io_uring_zcrx_area_reg area_reg = {
.addr = uring_ptr_to_u64(area + 1),
.len = AREA_SZ,
.flags = 0,
};
struct io_uring_zcrx_ifq_reg reg = {
.if_idx = ifidx,
.if_rxq = rxq,
.rq_entries = RQ_ENTRIES,
.area_ptr = uring_ptr_to_u64(&area_reg),
};
if (!try_register_ifq(®))
return T_EXIT_FAIL;
area_reg.addr = uring_ptr_to_u64(area);
area_reg.len = AREA_SZ - 1;
ret = try_register_ifq(®);
return ret ? T_EXIT_PASS : T_EXIT_FAIL;
}
static int test_larger_than_alloc_area(void *area)
{
int ret;
struct io_uring_zcrx_area_reg area_reg = {
.addr = uring_ptr_to_u64(area),
.len = AREA_SZ + 4096,
.flags = 0,
};
struct io_uring_zcrx_ifq_reg reg = {
.if_idx = ifidx,
.if_rxq = rxq,
.rq_entries = RQ_ENTRIES,
.area_ptr = uring_ptr_to_u64(&area_reg),
};
ret = try_register_ifq(®);
return ret ? T_EXIT_PASS : T_EXIT_FAIL;
}
static int test_area_access(void)
{
struct io_uring_zcrx_area_reg area_reg = {
.len = AREA_SZ,
.flags = 0,
};
struct io_uring_zcrx_ifq_reg reg = {
.if_idx = ifidx,
.if_rxq = rxq,
.rq_entries = RQ_ENTRIES,
.area_ptr = uring_ptr_to_u64(&area_reg),
};
int i, ret;
void *area;
for (i = 0; i < 2; i++) {
int ro = i & 1;
int prot = ro ? PROT_READ : PROT_WRITE;
area = mmap(NULL, AREA_SZ, prot,
MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0);
if (area == MAP_FAILED) {
perror("mmap");
return T_EXIT_FAIL;
}
area_reg.addr = uring_ptr_to_u64(area);
ret = try_register_ifq(®);
if (ret != -EFAULT) {
fprintf(stderr, "registered unaccessible memory\n");
return T_EXIT_FAIL;
}
munmap(area, AREA_SZ);
}
return T_EXIT_PASS;
}
static int create_ring_with_ifq(struct io_uring *ring, void *area, __u32 *id)
{
struct io_uring_zcrx_area_reg area_reg = {
.addr = uring_ptr_to_u64(area),
.len = AREA_SZ,
.flags = 0,
};
struct io_uring_zcrx_ifq_reg reg = {
.if_idx = ifidx,
.if_rxq = rxq,
.rq_entries = RQ_ENTRIES,
.area_ptr = uring_ptr_to_u64(&area_reg),
};
int ret;
ret = t_create_ring(128, ring, RING_FLAGS);
if (ret != T_SETUP_OK) {
fprintf(stderr, "ring create failed: %d\n", ret);
return T_EXIT_FAIL;
}
ret = io_uring_register_ifq(ring, ®);
if (ret) {
io_uring_queue_exit(ring);
fprintf(stderr, "ifq register failed %d\n", ret);
return T_EXIT_FAIL;
}
*id = reg.zcrx_id;
return 0;
}
static void test_io_uring_prep_zcrx(struct io_uring_sqe *sqe, int fd, int ifq)
{
io_uring_prep_rw(IORING_OP_RECV_ZC, sqe, fd, NULL, 0, 0);
sqe->zcrx_ifq_idx = ifq;
sqe->ioprio |= IORING_RECV_MULTISHOT;
}
static struct io_uring_cqe *submit_and_wait_one(struct io_uring *ring)
{
struct io_uring_cqe *cqe;
int ret;
ret = io_uring_submit(ring);
if (ret != 1) {
fprintf(stderr, "sqe submit failed: %d\n", ret);
return NULL;
}
ret = io_uring_wait_cqe(ring, &cqe);
if (ret < 0) {
fprintf(stderr, "wait completion %d\n", ret);
return NULL;
}
return cqe;
}
static int test_invalid_zcrx_request(void *area)
{
struct io_uring_cqe *cqe;
struct io_uring_sqe *sqe;
struct io_uring ring;
__u32 zcrx_id;
int ret, fds[2];
ret = create_ring_with_ifq(&ring, area, &zcrx_id);
if (ret != T_SETUP_OK) {
fprintf(stderr, "ifq-ring create failed: %d\n", ret);
return T_EXIT_FAIL;
}
ret = t_create_socket_pair(fds, true);
if (ret) {
fprintf(stderr, "t_create_socket_pair failed: %d\n", ret);
return ret;
}
sqe = io_uring_get_sqe(&ring);
test_io_uring_prep_zcrx(sqe, ring.ring_fd, zcrx_id);
cqe = submit_and_wait_one(&ring);
if (!cqe) {
fprintf(stderr, "submit_and_wait_one failed\n");
return T_EXIT_FAIL;
}
if (cqe->flags & IORING_CQE_F_MORE) {
fprintf(stderr, "unexpected F_MORE for invalid fd\n");
return T_EXIT_FAIL;
}
if (cqe->res != -ENOTSOCK) {
fprintf(stderr, "zcrx for non-socket file\n");
return T_EXIT_FAIL;
}
io_uring_cqe_seen(&ring, cqe);
sqe = io_uring_get_sqe(&ring);
test_io_uring_prep_zcrx(sqe, fds[0], zcrx_id + 1);
cqe = submit_and_wait_one(&ring);
if (!cqe) {
fprintf(stderr, "submit_and_wait_one failed\n");
return T_EXIT_FAIL;
}
if (cqe->flags & IORING_CQE_F_MORE) {
fprintf(stderr, "unexpected F_MORE for invalid fd\n");
return T_EXIT_FAIL;
}
if (cqe->res != -EINVAL) {
fprintf(stderr, "zcrx recv with non-existent zcrx ifq\n");
return T_EXIT_FAIL;
}
io_uring_cqe_seen(&ring, cqe);
close(fds[0]);
close(fds[1]);
io_uring_queue_exit(&ring);
return 0;
}
struct recv_data {
pthread_barrier_t connect;
pthread_barrier_t startup;
pthread_barrier_t barrier;
pthread_barrier_t finish;
int accept_fd;
char buf[MSG_SIZE];
void *area;
void *ring_ptr;
unsigned int ring_sz;
struct io_uring_zcrx_rq rq_ring;
};
static int recv_prep(struct io_uring *ring, struct recv_data *rd, int *sock,
__u32 zcrx_id)
{
struct sockaddr_in saddr;
struct io_uring_sqe *sqe;
int sockfd, ret, val, use_fd;
socklen_t socklen;
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
saddr.sin_port = htons(PORT);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("socket");
return 1;
}
val = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
ret = bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
if (ret < 0) {
perror("bind");
goto err;
}
ret = listen(sockfd, 1);
if (ret < 0) {
perror("listen");
goto err;
}
pthread_barrier_wait(&rd->connect);
socklen = sizeof(saddr);
use_fd = accept(sockfd, (struct sockaddr *)&saddr, &socklen);
if (use_fd < 0) {
perror("accept");
goto err;
}
rd->accept_fd = use_fd;
pthread_barrier_wait(&rd->startup);
pthread_barrier_wait(&rd->barrier);
sqe = io_uring_get_sqe(ring);
io_uring_prep_rw(IORING_OP_RECV_ZC, sqe, use_fd, NULL, 0, 0);
sqe->zcrx_ifq_idx = zcrx_id;
sqe->ioprio |= IORING_RECV_MULTISHOT;
sqe->user_data = 2;
ret = io_uring_submit(ring);
if (ret <= 0) {
fprintf(stderr, "submit failed: %d\n", ret);
goto err;
}
*sock = sockfd;
return 0;
err:
close(sockfd);
return 1;
}
static struct io_uring_zcrx_rqe* get_refill_entry(struct io_uring_zcrx_rq *rq_ring)
{
unsigned mask = rq_ring->ring_entries - 1;
struct io_uring_zcrx_rqe* rqe;
rqe = &rq_ring->rqes[rq_ring->rq_tail & mask];
rq_ring->rq_tail++;
return rqe;
}
static void refill_garbage(struct recv_data *rd, uint64_t area_token)
{
struct io_uring_zcrx_rq *rq_ring = &rd->rq_ring;
struct io_uring_zcrx_rqe* rqe;
int i = 0;
rqe = get_refill_entry(rq_ring);
rqe->off = (area_token + 1) << IORING_ZCRX_AREA_SHIFT;
i++;
rqe = get_refill_entry(rq_ring);
rqe->off = AREA_SZ | (area_token << IORING_ZCRX_AREA_SHIFT);
rqe->off += AREA_SZ;
i++;
for (; i < rq_ring->ring_entries; i++) {
rqe = get_refill_entry(rq_ring);
rqe->off = ((uint64_t)1 << IORING_ZCRX_AREA_SHIFT) - 1;
}
io_uring_smp_store_release(rq_ring->ktail, rq_ring->rq_tail);
}
static int do_recv(struct io_uring *ring, struct recv_data *rd,
uint64_t refill_area_token)
{
struct io_uring_cqe *cqe;
struct io_uring_zcrx_cqe *zcqe;
int i, ret;
refill_garbage(rd, refill_area_token);
for (i = 0; i < LOOP_COUNT - 1; i++) {
uint64_t off, mask = (1ULL << IORING_ZCRX_AREA_SHIFT) - 1;
void *addr;
ret = io_uring_wait_cqe(ring, &cqe);
if (ret) {
fprintf(stdout, "wait_cqe: %d\n", ret);
return 1;
}
if (cqe->res == -EINVAL) {
fprintf(stdout, "recv not supported, skipping\n");
goto out;
}
if (cqe->res < 0) {
fprintf(stderr, "failed recv cqe: %d\n", cqe->res);
goto err;
}
if (cqe->res - 1 != strlen(str)) {
fprintf(stderr, "got wrong length: %d/%d\n", cqe->res,
(int) strlen(str) + 1);
goto err;
}
zcqe = (struct io_uring_zcrx_cqe *)(cqe + 1);
off = zcqe->off & mask;
addr = (char *) rd->area + off;
ret = strncmp(str, addr, sizeof(str));
if (ret != 0) {
fprintf(stderr, "recv incorrect payload: %s\n", (const char *)addr);
goto err;
}
io_uring_cqe_seen(ring, cqe);
}
ret = io_uring_wait_cqe(ring, &cqe);
if (ret) {
fprintf(stdout, "wait_cqe: %d\n", ret);
return 1;
}
if (cqe->res != -ENOMEM) {
fprintf(stdout, "final recv cqe did not return ENOMEM\n");
goto err;
}
out:
io_uring_cqe_seen(ring, cqe);
pthread_barrier_wait(&rd->finish);
return 0;
err:
io_uring_cqe_seen(ring, cqe);
pthread_barrier_wait(&rd->finish);
return 1;
}
static void *recv_fn(void *data)
{
struct recv_data *rd = data;
struct io_uring_params p = { };
struct io_uring ring;
int ret, sock;
struct io_uring_zcrx_area_reg area_reg = {
.addr = uring_ptr_to_u64(rd->area),
.len = AREA_SZ,
.flags = 0,
};
struct io_uring_zcrx_ifq_reg reg = {
.if_idx = ifidx,
.if_rxq = rxq,
.rq_entries = RQ_ENTRIES,
.area_ptr = uring_ptr_to_u64(&area_reg),
};
p.flags = RING_FLAGS;
ret = t_create_ring_params(8, &ring, &p);
if (ret == T_SETUP_SKIP) {
ret = 0;
goto err;
} else if (ret < 0) {
goto err;
}
ret = io_uring_register_ifq(&ring, ®);
if (ret != 0) {
fprintf(stderr, "register_ifq failed: %d\n", ret);
goto err_ring_exit;
}
rd->rq_ring.khead = (__u32*)((char*)rd->ring_ptr + reg.offsets.head);
rd->rq_ring.ktail = (__u32*)((char*)rd->ring_ptr + reg.offsets.tail);
rd->rq_ring.rqes = (struct io_uring_zcrx_rqe*)((char*)rd->ring_ptr + reg.offsets.rqes);
rd->rq_ring.rq_tail = 0;
rd->rq_ring.ring_entries = reg.rq_entries;
ret = recv_prep(&ring, rd, &sock, reg.zcrx_id);
if (ret) {
fprintf(stderr, "recv_prep failed: %d\n", ret);
goto err;
}
ret = do_recv(&ring, rd, area_reg.rq_area_token);
close(sock);
close(rd->accept_fd);
err_ring_exit:
io_uring_queue_exit(&ring);
err:
return (void *)(intptr_t)ret;
}
static int do_send(struct recv_data *rd)
{
struct sockaddr_in saddr;
struct iovec iov = {
.iov_base = str,
.iov_len = sizeof(str),
};
struct io_uring ring;
struct io_uring_cqe *cqe;
struct io_uring_sqe *sqe;
int i, sockfd, ret;
ret = io_uring_queue_init(8, &ring, 0);
if (ret) {
fprintf(stderr, "queue init failed: %d\n", ret);
return 1;
}
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(PORT);
inet_pton(AF_INET, HOST, &saddr.sin_addr);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("socket");
goto err2;
}
pthread_barrier_wait(&rd->connect);
ret = connect(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
if (ret < 0) {
perror("connect");
goto err;
}
pthread_barrier_wait(&rd->startup);
for (i = 0; i < LOOP_COUNT; i++) {
sqe = io_uring_get_sqe(&ring);
io_uring_prep_send(sqe, sockfd, iov.iov_base, iov.iov_len, 0);
sqe->user_data = 1;
}
ret = io_uring_submit(&ring);
if (ret <= 0) {
fprintf(stderr, "submit failed: %d\n", ret);
goto err;
}
pthread_barrier_wait(&rd->barrier);
ret = io_uring_wait_cqe(&ring, &cqe);
if (cqe->res == -EINVAL) {
fprintf(stdout, "send not supported, skipping\n");
goto err;
}
if (cqe->res != iov.iov_len) {
fprintf(stderr, "failed cqe: %d\n", cqe->res);
goto err;
}
pthread_barrier_wait(&rd->finish);
close(sockfd);
io_uring_queue_exit(&ring);
return 0;
err:
close(sockfd);
err2:
io_uring_queue_exit(&ring);
pthread_barrier_wait(&rd->finish);
return 1;
}
static int test_recv(void *area)
{
pthread_t recv_thread;
struct recv_data rd;
int ret;
void *retval;
memset(&rd, 0, sizeof(rd));
pthread_barrier_init(&rd.connect, NULL, 2);
pthread_barrier_init(&rd.startup, NULL, 2);
pthread_barrier_init(&rd.barrier, NULL, 2);
pthread_barrier_init(&rd.finish, NULL, 2);
rd.area = area;
ret = pthread_create(&recv_thread, NULL, recv_fn, &rd);
if (ret) {
fprintf(stderr, "Thread create failed: %d\n", ret);
return 1;
}
do_send(&rd);
pthread_join(recv_thread, &retval);
return (intptr_t)retval;
}
int main(int argc, char *argv[])
{
char *dev, *rxq_str, *rxq_end;
void *area_outer, *area;
int ret;
if (argc > 1)
return T_EXIT_SKIP;
page_size = sysconf(_SC_PAGESIZE);
if (page_size < 0) {
perror("sysconf(_SC_PAGESIZE)");
return T_EXIT_FAIL;
}
area_outer = mmap(NULL, AREA_SZ + 8192, PROT_NONE,
MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE, -1, 0);
if (area_outer == MAP_FAILED) {
perror("mmap");
return T_EXIT_FAIL;
}
area = mmap(area_outer, AREA_SZ, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0);
if (area == MAP_FAILED) {
perror("mmap");
return T_EXIT_FAIL;
}
dev = getenv(DEV_ENV_VAR);
if (!dev)
return T_EXIT_SKIP;
ifidx = if_nametoindex(dev);
if (!ifidx)
return T_EXIT_SKIP;
rxq_str = getenv(RXQ_ENV_VAR);
if (!rxq_str)
return T_EXIT_SKIP;
rxq = strtol(rxq_str, &rxq_end, 10);
if (rxq_end == rxq_str || *rxq_end != '\0')
return T_EXIT_SKIP;
ret = probe_zcrx(area);
if (ret != T_EXIT_PASS)
return ret;
ret = test_rq_setup(area);
if (ret) {
fprintf(stderr, "test_invalid_reg_struct failed\n");
return ret;
}
ret = test_null_area_reg_struct();
if (ret) {
fprintf(stderr, "test_null_area_reg_struct failed\n");
return ret;
}
ret = test_null_area();
if (ret) {
fprintf(stderr, "test_null_area failed\n");
return ret;
}
ret = test_misaligned_area(area);
if (ret) {
fprintf(stderr, "test_misaligned_area failed\n");
return ret;
}
ret = test_larger_than_alloc_area(area);
if (ret) {
fprintf(stderr, "test_larger_than_alloc_area failed\n");
return ret;
}
ret = test_area_access();
if (ret) {
fprintf(stderr, "test_area_access failed\n");
return ret;
}
ret = test_invalid_if(area);
if (ret) {
fprintf(stderr, "test_invalid_if failed\n");
return ret;
}
ret = test_invalid_ifq_collision(area);
if (ret) {
fprintf(stderr, "test_invalid_ifq_collision failed\n");
return ret;
}
ret = test_invalid_zcrx_request(area);
if (ret) {
fprintf(stderr, "test_invalid_ifq_collision failed\n");
return ret;
}
ret = test_recv(area);
if (ret) {
fprintf(stderr, "test_recv failed\n");
return ret;
}
munmap(area, AREA_SZ);
return T_EXIT_PASS;
}