You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
157 lines
3.4 KiB
157 lines
3.4 KiB
/* 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;
|
|
}
|