#include "../config-host.h" /* SPDX-License-Identifier: MIT */ /* * Description: test ring resizing with fixed large sqes/cqes, and with * mixed mode sqes/cqes. */ #include #include #include #include #include #include #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; }