| /* SPDX-License-Identifier: MIT */ |
| /* |
| * Description: test ring resizing with fixed large sqes/cqes, and with |
| * mixed mode sqes/cqes. |
| */ |
| #include <stdio.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| |
| #include "liburing.h" |
| #include "helpers.h" |
| |
| static int test_cqe32_resize(void) |
| { |
| struct io_uring ring; |
| struct io_uring_params p = { }, new_p = { }; |
| struct io_uring_cqe *cqe; |
| struct io_uring_sqe *sqe; |
| int ret, i; |
| |
| p.flags = IORING_SETUP_CQE32 | IORING_SETUP_DEFER_TASKRUN | |
| IORING_SETUP_SINGLE_ISSUER; |
| p.sq_entries = 8; |
| p.cq_entries = 8; |
| |
| ret = io_uring_queue_init_params(8, &ring, &p); |
| if (ret) { |
| if (ret == -EINVAL) |
| return T_EXIT_SKIP; |
| fprintf(stderr, "queue_init: %s\n", strerror(-ret)); |
| return T_EXIT_FAIL; |
| } |
| |
| for (i = 0; i < 4; i++) { |
| sqe = io_uring_get_sqe(&ring); |
| io_uring_prep_nop(sqe); |
| sqe->user_data = 0xAAAA0000ULL + i; |
| } |
| |
| ret = io_uring_submit(&ring); |
| if (ret != 4) { |
| fprintf(stderr, "submit: %d\n", ret); |
| return T_EXIT_FAIL; |
| } |
| |
| /* Resize the ring */ |
| new_p.sq_entries = 8; |
| new_p.cq_entries = 16; |
| new_p.flags = IORING_SETUP_CQSIZE; |
| |
| ret = io_uring_resize_rings(&ring, &new_p); |
| if (ret) { |
| if (ret == -EINVAL) |
| return T_EXIT_SKIP; |
| fprintf(stderr, "resize failed: %s\n", strerror(-ret)); |
| return T_EXIT_FAIL; |
| } |
| |
| for (i = 0; i < 4; i++) { |
| ret = io_uring_wait_cqe(&ring, &cqe); |
| if (ret) { |
| fprintf(stderr, "wait_cqe: %s\n", strerror(-ret)); |
| return T_EXIT_FAIL; |
| } |
| |
| if (cqe->user_data != (0xAAAA0000ULL + i)) { |
| fprintf(stderr, " *** CQE32 CORRUPTION DETECTED ***\n"); |
| return T_EXIT_FAIL; |
| } |
| |
| io_uring_cqe_seen(&ring, cqe); |
| } |
| |
| io_uring_queue_exit(&ring); |
| return T_EXIT_PASS; |
| } |
| |
| static int test_cqe_mixed_resize(void) |
| { |
| struct io_uring ring; |
| struct io_uring_params p = { }, new_p = { }; |
| struct io_uring_cqe *cqe; |
| struct io_uring_sqe *sqe; |
| int ret, i; |
| |
| /* Setup ring with CQE_MIXED, DEFER_TASKRUN, SINGLE_ISSUER */ |
| p.flags = IORING_SETUP_CQE_MIXED | IORING_SETUP_DEFER_TASKRUN | |
| IORING_SETUP_SINGLE_ISSUER; |
| p.sq_entries = 8; |
| p.cq_entries = 8; |
| |
| ret = io_uring_queue_init_params(8, &ring, &p); |
| if (ret) { |
| if (ret == -EINVAL) |
| return T_EXIT_SKIP; |
| fprintf(stderr, "ring_init: %d\n", ret); |
| return T_EXIT_FAIL; |
| } |
| |
| for (i = 0; i < 4; i++) { |
| sqe = io_uring_get_sqe(&ring); |
| if (!sqe) { |
| fprintf(stderr, "get_sqe failed\n"); |
| return T_EXIT_FAIL; |
| } |
| |
| if (i % 2 == 0) { |
| io_uring_prep_nop(sqe); |
| sqe->user_data = 0xBBBB0000ULL + i; |
| } else { |
| io_uring_prep_nop(sqe); /* Still NOP but different pattern */ |
| sqe->user_data = 0xCCCC0000ULL + i; |
| } |
| } |
| |
| ret = io_uring_submit(&ring); |
| if (ret != 4) { |
| fprintf(stderr, "submit: %d\n", ret); |
| return T_EXIT_FAIL; |
| } |
| |
| /* Resize the ring */ |
| new_p.sq_entries = 8; |
| new_p.cq_entries = 16; |
| new_p.flags = IORING_SETUP_CQSIZE; |
| |
| ret = io_uring_resize_rings(&ring, &new_p); |
| if (ret) { |
| fprintf(stderr, "resize failed: %s\n", strerror(-ret)); |
| return T_EXIT_FAIL; |
| } |
| |
| /* Reap and verify CQEs */ |
| for (i = 0; i < 4; i++) { |
| uint64_t expected; |
| |
| ret = io_uring_wait_cqe(&ring, &cqe); |
| if (ret) { |
| fprintf(stderr, "wait_cqe: %s\n", strerror(-ret)); |
| return T_EXIT_FAIL; |
| } |
| |
| expected = (i % 2 == 0) ? (0xBBBB0000ULL + i) : (0xCCCC0000ULL + i); |
| if (cqe->user_data != expected) { |
| fprintf(stderr, " *** CQE_MIXED CORRUPTION DETECTED ***\n"); |
| return T_EXIT_FAIL; |
| } |
| |
| io_uring_cqe_seen(&ring, cqe); |
| } |
| |
| io_uring_queue_exit(&ring); |
| return T_EXIT_PASS; |
| } |
| |
| static int test_ring_wrapping(void) |
| { |
| struct io_uring ring; |
| struct io_uring_params p = { }, new_p = { }; |
| struct io_uring_cqe *cqe; |
| struct io_uring_sqe *sqe; |
| int ret, i, consumed_count; |
| int remaining_expected[] = {6, 7}; |
| int remaining_count = 0; |
| |
| /* Setup small ring to force wrapping */ |
| p.flags = IORING_SETUP_CQE32 | IORING_SETUP_DEFER_TASKRUN | |
| IORING_SETUP_SINGLE_ISSUER; |
| p.sq_entries = 4; |
| p.cq_entries = 4; |
| |
| ret = io_uring_queue_init_params(4, &ring, &p); |
| if (ret) { |
| if (ret == -EINVAL) |
| return T_EXIT_SKIP; |
| fprintf(stderr, "queue_init: %s\n", strerror(-ret)); |
| return T_EXIT_FAIL; |
| } |
| |
| /* Submit more entries to force head/tail wrapping */ |
| consumed_count = 0; |
| for (i = 0; i < 8; i++) { |
| sqe = io_uring_get_sqe(&ring); |
| if (!sqe) { |
| fprintf(stderr, "get_sqe failed at %d\n", i); |
| return T_EXIT_FAIL; |
| } |
| io_uring_prep_nop(sqe); |
| sqe->user_data = 0xDDDD0000ULL + i; |
| |
| ret = io_uring_submit(&ring); |
| if (ret != 1) { |
| fprintf(stderr, "submit failed: %d\n", ret); |
| return T_EXIT_FAIL; |
| } |
| |
| /* Consume some to create wrap scenario */ |
| if (i >= 2) { |
| uint64_t expected; |
| |
| ret = io_uring_wait_cqe(&ring, &cqe); |
| if (ret) { |
| fprintf(stderr, "wait_cqe: %s\n", strerror(-ret)); |
| return T_EXIT_FAIL; |
| } |
| |
| /* Verify early consumed CQE */ |
| expected = 0xDDDD0000ULL + consumed_count; |
| |
| if (cqe->user_data != expected) { |
| fprintf(stderr, " *** EARLY CQE CORRUPTION DETECTED ***\n"); |
| return T_EXIT_FAIL; |
| } |
| |
| io_uring_cqe_seen(&ring, cqe); |
| consumed_count++; |
| } |
| } |
| |
| /* Now resize with pending wrapped entries */ |
| new_p.sq_entries = 4; |
| new_p.cq_entries = 16; |
| |
| ret = io_uring_resize_rings(&ring, &new_p); |
| if (ret) { |
| fprintf(stderr, "resize failed: %s\n", strerror(-ret)); |
| return T_EXIT_FAIL; |
| } |
| |
| /* Consume remaining entries (should be operations 6 and 7) */ |
| while (true) { |
| uint64_t expected; |
| |
| ret = io_uring_peek_cqe(&ring, &cqe); |
| if (ret == -EAGAIN) |
| break; |
| if (ret) { |
| fprintf(stderr, "peek_cqe: %s\n", strerror(-ret)); |
| return T_EXIT_FAIL; |
| } |
| |
| if (remaining_count >= 2) { |
| printf(" *** TOO MANY REMAINING CQES ***\n"); |
| return T_EXIT_FAIL; |
| } |
| |
| expected = 0xDDDD0000ULL + remaining_expected[remaining_count]; |
| if (cqe->user_data != expected) { |
| fprintf(stderr, " *** REMAINING CQE CORRUPTION DETECTED ***\n"); |
| return T_EXIT_FAIL; |
| } |
| |
| io_uring_cqe_seen(&ring, cqe); |
| remaining_count++; |
| } |
| |
| if (remaining_count != 2) { |
| fprintf(stderr, " *** WRONG NUMBER OF REMAINING CQES: got %d, expected 2 ***\n", remaining_count); |
| return T_EXIT_FAIL; |
| } |
| |
| io_uring_queue_exit(&ring); |
| return T_EXIT_PASS; |
| } |
| |
| static int test_sqe128_resize(void) |
| { |
| struct io_uring ring; |
| struct io_uring_params p = { }, new_p = { }; |
| struct io_uring_cqe *cqe; |
| struct io_uring_sqe *sqe; |
| int ret, i; |
| |
| p.flags = IORING_SETUP_SQE128 | IORING_SETUP_DEFER_TASKRUN | |
| IORING_SETUP_SINGLE_ISSUER; |
| p.sq_entries = 8; |
| p.cq_entries = 8; |
| |
| ret = io_uring_queue_init_params(8, &ring, &p); |
| if (ret) { |
| if (ret == -EINVAL) |
| return T_EXIT_SKIP; |
| fprintf(stderr, "queue_init: %s\n", strerror(-ret)); |
| return T_EXIT_FAIL; |
| } |
| |
| /* Prepare 4 large SQEs but don't submit them yet */ |
| for (i = 0; i < 4; i++) { |
| sqe = io_uring_get_sqe(&ring); |
| if (!sqe) { |
| fprintf(stderr, "get_sqe failed\n"); |
| return 1; |
| } |
| io_uring_prep_nop(sqe); |
| sqe->user_data = 0xEEEE0000ULL + i; |
| |
| /* Fill some extended SQE data to ensure 128-byte copy works */ |
| if (ring.sq.ring_ptr) { |
| /* Access extended part of SQE128 if available */ |
| memset(sqe->cmd, 0x55 + i, sizeof(sqe->cmd)); |
| } |
| } |
| |
| __io_uring_flush_sq(&ring); |
| |
| /* Resize the ring while SQEs are pending */ |
| new_p.sq_entries = 16; |
| new_p.cq_entries = 16; |
| new_p.flags = IORING_SETUP_CQSIZE; |
| |
| ret = io_uring_resize_rings(&ring, &new_p); |
| if (ret) { |
| fprintf(stderr, "resize failed: %s\n", strerror(-ret)); |
| return 1; |
| } |
| |
| /* Now submit the pending SQEs */ |
| ret = io_uring_submit(&ring); |
| if (ret != 4) { |
| fprintf(stderr, "submit after resize: %d (expected 4)\n", ret); |
| return T_EXIT_FAIL; |
| } |
| |
| /* Reap and verify CQEs */ |
| for (i = 0; i < 4; i++) { |
| ret = io_uring_wait_cqe(&ring, &cqe); |
| if (ret) { |
| fprintf(stderr, "wait_cqe: %s\n", strerror(-ret)); |
| return T_EXIT_FAIL; |
| } |
| |
| if (cqe->user_data != (0xEEEE0000ULL + i)) { |
| printf(" *** SQE128 CORRUPTION DETECTED ***\n"); |
| return T_EXIT_FAIL; |
| } |
| |
| io_uring_cqe_seen(&ring, cqe); |
| } |
| |
| io_uring_queue_exit(&ring); |
| return T_EXIT_PASS; |
| } |
| |
| static int test_sqe_mixed_resize(void) |
| { |
| struct io_uring ring; |
| struct io_uring_params p = { }, new_p = { }; |
| struct io_uring_cqe *cqe; |
| struct io_uring_sqe *sqe; |
| int ret, i; |
| |
| p.flags = IORING_SETUP_SQE_MIXED | IORING_SETUP_DEFER_TASKRUN | |
| IORING_SETUP_SINGLE_ISSUER; |
| p.sq_entries = 8; |
| p.cq_entries = 8; |
| |
| ret = io_uring_queue_init_params(8, &ring, &p); |
| if (ret) { |
| if (ret == -EINVAL) |
| return T_EXIT_SKIP; |
| return T_EXIT_FAIL; |
| } |
| |
| /* Prepare mix of regular and large SQEs but don't submit them yet */ |
| for (i = 0; i < 4; i++) { |
| sqe = io_uring_get_sqe(&ring); |
| if (!sqe) { |
| fprintf(stderr, "get_sqe failed\n"); |
| return T_EXIT_FAIL; |
| } |
| |
| /* Alternate between regular NOPs and operations that might use large SQEs */ |
| if (i % 2 == 0) { |
| io_uring_prep_nop(sqe); |
| sqe->user_data = 0xFFFF0000ULL + i; |
| } else { |
| io_uring_prep_nop(sqe); /* Still NOP but different pattern */ |
| sqe->user_data = 0x11110000ULL + i; |
| /* Fill extended data for mixed SQE */ |
| memset(sqe->cmd, 0xAA + i, sizeof(sqe->cmd)); |
| } |
| } |
| |
| __io_uring_flush_sq(&ring); |
| |
| /* Resize the ring while mixed SQEs are pending */ |
| new_p.sq_entries = 16; |
| new_p.cq_entries = 16; |
| new_p.flags = IORING_SETUP_CQSIZE; |
| |
| ret = io_uring_resize_rings(&ring, &new_p); |
| if (ret) { |
| fprintf(stderr, "resize failed: %s\n", strerror(-ret)); |
| return T_EXIT_FAIL; |
| } |
| |
| /* Now submit the pending mixed SQEs */ |
| ret = io_uring_submit(&ring); |
| if (ret != 4) { |
| fprintf(stderr, "submit after resize: %d (expected 4)\n", ret); |
| return T_EXIT_FAIL; |
| } |
| |
| /* Reap and verify CQEs */ |
| for (i = 0; i < 4; i++) { |
| uint64_t expected; |
| |
| ret = io_uring_wait_cqe(&ring, &cqe); |
| if (ret) { |
| fprintf(stderr, "wait_cqe: %s\n", strerror(-ret)); |
| return T_EXIT_FAIL; |
| } |
| |
| expected = (i % 2 == 0) ? (0xFFFF0000ULL + i) : (0x11110000ULL + i); |
| if (cqe->user_data != expected) { |
| fprintf(stderr, " *** SQE_MIXED CORRUPTION DETECTED ***\n"); |
| return T_EXIT_FAIL; |
| } |
| |
| io_uring_cqe_seen(&ring, cqe); |
| } |
| |
| io_uring_queue_exit(&ring); |
| return T_EXIT_PASS; |
| } |
| |
| int main(void) |
| { |
| int ret; |
| |
| ret = test_cqe32_resize(); |
| if (ret == T_EXIT_FAIL) { |
| fprintf(stderr, "cqe32_resize failed\n"); |
| return T_EXIT_FAIL; |
| } else if (ret == T_EXIT_SKIP) { |
| return T_EXIT_SKIP; |
| } |
| |
| ret = test_cqe_mixed_resize(); |
| if (ret == T_EXIT_FAIL) { |
| fprintf(stderr, "cqe_mixed_resize failed\n"); |
| return T_EXIT_FAIL; |
| } else if (ret == T_EXIT_SKIP) { |
| return T_EXIT_SKIP; |
| } |
| |
| ret = test_sqe128_resize(); |
| if (ret == T_EXIT_FAIL) { |
| fprintf(stderr, "sqe128_resize failed\n"); |
| return T_EXIT_FAIL; |
| } else if (ret == T_EXIT_SKIP) { |
| return T_EXIT_SKIP; |
| } |
| |
| ret = test_ring_wrapping(); |
| if (ret == T_EXIT_FAIL) { |
| fprintf(stderr, "ring_wrapping failed\n"); |
| return T_EXIT_FAIL; |
| } else if (ret == T_EXIT_SKIP) { |
| return T_EXIT_SKIP; |
| } |
| |
| ret = test_sqe_mixed_resize(); |
| if (ret == T_EXIT_FAIL) { |
| fprintf(stderr, "sqe_mixed_resize failed\n"); |
| return T_EXIT_FAIL; |
| } |
| |
| return T_EXIT_PASS; |
| } |