123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 |
- #include "../config-host.h"
- /* SPDX-License-Identifier: MIT */
- /*
- * Description: tests linked requests failing during submission
- */
- #include <errno.h>
- #include <stdio.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <string.h>
- #include <fcntl.h>
- #include <assert.h>
- #include "liburing.h"
- #define DRAIN_USER_DATA 42
- static int test_underprep_fail(bool hardlink, bool drain, bool link_last,
- int link_size, int fail_idx)
- {
- const int invalid_fd = 42;
- int link_flags = IOSQE_IO_LINK;
- int total_submit = link_size;
- struct io_uring ring;
- struct io_uring_sqe *sqe;
- struct io_uring_cqe *cqe;
- char buffer[1] = { };
- int i, ret, fds[2];
- if (drain)
- link_flags |= IOSQE_IO_DRAIN;
- if (hardlink)
- link_flags |= IOSQE_IO_HARDLINK;
- assert(fail_idx < link_size);
- assert(link_size < 40);
- /* create a new ring as it leaves it dirty */
- ret = io_uring_queue_init(8, &ring, 0);
- if (ret) {
- printf("ring setup failed\n");
- return -1;
- }
- if (pipe(fds)) {
- perror("pipe");
- return -1;
- }
- if (drain) {
- /* clog drain, so following reqs sent to draining */
- sqe = io_uring_get_sqe(&ring);
- io_uring_prep_read(sqe, fds[0], buffer, sizeof(buffer), 0);
- sqe->user_data = DRAIN_USER_DATA;
- sqe->flags |= IOSQE_IO_DRAIN;
- total_submit++;
- }
- for (i = 0; i < link_size; i++) {
- sqe = io_uring_get_sqe(&ring);
- if (i == fail_idx) {
- io_uring_prep_read(sqe, invalid_fd, buffer, 1, 0);
- sqe->ioprio = (short) -1;
- } else {
- io_uring_prep_nop(sqe);
- }
- if (i != link_size - 1 || !link_last)
- sqe->flags |= link_flags;
- sqe->user_data = i;
- }
- ret = io_uring_submit(&ring);
- if (ret != total_submit) {
- /* Old behaviour, failed early and under-submitted */
- if (ret == fail_idx + 1 + drain)
- goto out;
- fprintf(stderr, "submit failed: %d\n", ret);
- return -1;
- }
- if (drain) {
- /* unclog drain */
- ret = write(fds[1], buffer, sizeof(buffer));
- if (ret < 0) {
- perror("write");
- return 1;
- }
- }
- for (i = 0; i < total_submit; i++) {
- ret = io_uring_wait_cqe(&ring, &cqe);
- if (ret) {
- fprintf(stderr, "wait_cqe=%d\n", ret);
- return 1;
- }
- ret = cqe->res;
- if (cqe->user_data == DRAIN_USER_DATA) {
- if (ret != 1) {
- fprintf(stderr, "drain failed %d\n", ret);
- return 1;
- }
- } else if (cqe->user_data == fail_idx) {
- if (ret == 0 || ret == -ECANCELED) {
- fprintf(stderr, "half-prep req unexpected return %d\n", ret);
- return 1;
- }
- } else {
- if (ret != -ECANCELED) {
- fprintf(stderr, "cancel failed %d, ud %d\n", ret, (int)cqe->user_data);
- return 1;
- }
- }
- io_uring_cqe_seen(&ring, cqe);
- }
- out:
- close(fds[0]);
- close(fds[1]);
- io_uring_queue_exit(&ring);
- return 0;
- }
- int main(int argc, char *argv[])
- {
- int ret, link_size, fail_idx, i;
- if (argc > 1)
- return 0;
- /*
- * hardlink, size=3, fail_idx=1, drain=false -- kernel fault
- * link, size=3, fail_idx=0, drain=true -- kernel fault
- * link, size=3, fail_idx=1, drain=true -- invalid cqe->res
- */
- for (link_size = 0; link_size < 3; link_size++) {
- for (fail_idx = 0; fail_idx < link_size; fail_idx++) {
- for (i = 0; i < 8; i++) {
- bool hardlink = (i & 1) != 0;
- bool drain = (i & 2) != 0;
- bool link_last = (i & 4) != 0;
- ret = test_underprep_fail(hardlink, drain, link_last,
- link_size, fail_idx);
- if (!ret)
- continue;
- fprintf(stderr, "failed %d, hard %d, drain %d,"
- "link_last %d, size %d, idx %d\n",
- ret, hardlink, drain, link_last,
- link_size, fail_idx);
- return 1;
- }
- }
- }
- return 0;
- }
|