[PATCH v2 1/4] io_uring: Redesign common helpers to follow liburing API conventions
Sebastian Chlad <[email protected]>
| Newsgroups | gmane.linux.ltp |
|---|---|
| Message-ID | <[email protected]> |
Replace coupled submit and wait helpers with a proper liburing-style API: - io_uring_get_sqe(): get next SQE slot from the submission ring - io_uring_prep_rw(): generic SQE fill for read/write operations - io_uring_prep_read/write/readv/writev(): operation-specific prep helpers - io_uring_sqe_set_data64(): set user_data explicitly on the SQE - io_uring_submit(): submit all pending SQEs to the kernel - io_uring_cqe_wait(): wait for next CQE, return pointer without consuming it - io_uring_cqe_seen(): advance the CQ head to mark a CQE as consumed With the new API, each test controls SQE preparation, user_data assignment, submission, CQE validation and result reporting explicitly in its own body, matching how real io_uring applications are written. Signed-off-by: Sebastian Chlad <sebastian.chlad-IBi9RG/[email protected]> --- Changes in v2: - Keep deprecated helpers alongside new ones for bisectability (removal moved to patch 4/4) - Since doing the above, the io_uring_wait_cqe() had to be renames so it is renamed to io_uring_cqe_wait() to avoid name conflict with the pre-existing io_uring_wait_cqe() helper - Use __atomic_store/__atomic_load with __ATOMIC_RELEASE/__ATOMIC_ACQUIRE .../syscalls/io_uring/io_uring_common.h | 126 +++++++++++++++++- 1 file changed, 125 insertions(+), 1 deletion(-) diff --git a/testcases/kernel/syscalls/io_uring/io_uring_common.h b/testcases/kernel/syscalls/io_uring/io_uring_common.h index aa31339fb..8ff024e5f 100644 --- a/testcases/kernel/syscalls/io_uring/io_uring_common.h +++ b/testcases/kernel/syscalls/io_uring/io_uring_common.h @@ -3,6 +3,8 @@ * Copyright (C) 2026 IBM * Author: Sachin Sant <[email protected]> * + * Copyright (C) 2026 Sebastian Chlad <sebastian.chlad-IBi9RG/[email protected]> + * * Common definitions and helper functions for io_uring tests */ @@ -16,7 +18,6 @@ #include "tst_test.h" #include "lapi/io_uring.h" -/* Common structures for io_uring ring management */ struct io_sq_ring { unsigned int *head; unsigned int *tail; @@ -43,6 +44,7 @@ struct io_uring_submit { size_t sq_ptr_size; void *cq_ptr; size_t cq_ptr_size; + unsigned int sq_pending; }; /* @@ -275,4 +277,126 @@ static inline void io_uring_do_vec_io_op(struct io_uring_submit *s, int fd, expected_size); } +/* + * Get the next available SQE slot from the submission ring. + * The SQE is zeroed and tracked as pending until io_uring_submit() is called. + */ +static inline struct io_uring_sqe *io_uring_get_sqe(struct io_uring_submit *s) +{ + struct io_sq_ring *sring = &s->sq_ring; + unsigned int index = (*sring->tail + s->sq_pending) & *sring->ring_mask; + struct io_uring_sqe *sqe = &s->sqes[index]; + + memset(sqe, 0, sizeof(*sqe)); + sring->array[index] = index; + s->sq_pending++; + + return sqe; +} + +/* + * Generic SQE fill for read/write family operations. + * Does not touch user_data - caller sets it via io_uring_sqe_set_data64(). + */ +static inline void io_uring_prep_rw(struct io_uring_sqe *sqe, int opcode, + int fd, const void *addr, unsigned int len, + off_t offset) +{ + sqe->opcode = opcode; + sqe->fd = fd; + sqe->addr = (unsigned long)addr; + sqe->len = len; + sqe->off = offset; +} + +static inline void io_uring_prep_read(struct io_uring_sqe *sqe, int fd, + void *buf, size_t len, off_t offset) +{ + io_uring_prep_rw(sqe, IORING_OP_READ, fd, buf, len, offset); +} + +static inline void io_uring_prep_write(struct io_uring_sqe *sqe, int fd, + const void *buf, size_t len, off_t offset) +{ + io_uring_prep_rw(sqe, IORING_OP_WRITE, fd, buf, len, offset); +} + +static inline void io_uring_prep_readv(struct io_uring_sqe *sqe, int fd, + struct iovec *iovs, int nr_vecs, + off_t offset) +{ + io_uring_prep_rw(sqe, IORING_OP_READV, fd, iovs, nr_vecs, offset); +} + +static inline void io_uring_prep_writev(struct io_uring_sqe *sqe, int fd, + struct iovec *iovs, int nr_vecs, + off_t offset) +{ + io_uring_prep_rw(sqe, IORING_OP_WRITEV, fd, iovs, nr_vecs, offset); +} + +/* + * Set the user_data field of an SQE. user_data is returned verbatim in the + * corresponding CQE and must be unique per in-flight request to allow correct + * correlation of completions. + */ +static inline void io_uring_sqe_set_data64(struct io_uring_sqe *sqe, + uint64_t data) +{ + sqe->user_data = data; +} + +/* + * Submit all pending SQEs to the kernel. + */ +static inline void io_uring_submit(struct io_uring_submit *s) +{ + unsigned int pending = s->sq_pending; + + if (!pending) + return; + + unsigned int tail = *s->sq_ring.tail + pending; + + __atomic_store(s->sq_ring.tail, &tail, __ATOMIC_RELEASE); + s->sq_pending = 0; + + if (io_uring_enter(s->ring_fd, pending, 0, 0, NULL) < 0) + tst_brk(TBROK | TERRNO, "io_uring_enter() failed"); +} + +/* + * Wait for the next CQE and return a pointer to it. + * Does not advance the CQ head - call io_uring_cqe_seen() when done. + */ +static inline struct io_uring_cqe *io_uring_cqe_wait(struct io_uring_submit *s, + sigset_t *sig) +{ + struct io_cq_ring *cring = &s->cq_ring; + int ret; + + ret = io_uring_enter(s->ring_fd, 0, 1, IORING_ENTER_GETEVENTS, sig); + if (ret < 0) + tst_brk(TBROK | TERRNO, "io_uring_enter() failed"); + + unsigned int cq_tail; + + __atomic_load(cring->tail, &cq_tail, __ATOMIC_ACQUIRE); + if (*cring->head == cq_tail) + tst_brk(TBROK, "No completion event received"); + + return &cring->cqes[*cring->head & *cring->ring_mask]; +} + +/* + * Mark the current CQE as consumed, advancing the CQ head. + */ +static inline void io_uring_cqe_seen(struct io_uring_submit *s) +{ + unsigned int head = *s->cq_ring.head + 1; + + __atomic_store(s->cq_ring.head, &head, __ATOMIC_RELEASE); +} + + #endif /* IO_URING_COMMON_H */ -- 2.51.0 -- Mailing list info: https://lists.linux.it/listinfo/ltp