/* SPDX-License-Identifier: MIT */ /* * Test reads that will punt to blocking context, with immediate overwrite * of iovec->iov_base to NULL. If the kernel doesn't properly handle * reuse of the iovec, we should get -EFAULT. */ #include #include #include #include #include #include #include #include "liburing.h" #define STR_SIZE 32768 #define FILE_SIZE 65536 struct thread_data { int fd1, fd2; volatile int do_exit; }; static void *flusher(void *__data) { struct thread_data *data = __data; int i = 0; while (!data->do_exit) { posix_fadvise(data->fd1, 0, FILE_SIZE, POSIX_FADV_DONTNEED); posix_fadvise(data->fd2, 0, FILE_SIZE, POSIX_FADV_DONTNEED); i++; } return NULL; } static int create_file(const char *file) { ssize_t ret; char *buf; int fd; buf = malloc(FILE_SIZE); memset(buf, 0xaa, FILE_SIZE); fd = open(file, O_WRONLY | O_CREAT, 0644); if (fd < 0) { perror("open file"); return 1; } ret = write(fd, buf, FILE_SIZE); fsync(fd); close(fd); return ret != FILE_SIZE; } static char str1[STR_SIZE]; static char str2[STR_SIZE]; static struct io_uring ring; static int prep(int fd, char *str) { struct io_uring_sqe *sqe; struct iovec iov = { .iov_base = str, .iov_len = STR_SIZE, }; int ret; sqe = io_uring_get_sqe(&ring); io_uring_prep_readv(sqe, fd, &iov, 1, 0); sqe->user_data = fd; ret = io_uring_submit(&ring); if (ret != 1) { fprintf(stderr, "submit got %d\n", ret); return 1; } iov.iov_base = NULL; return 0; } static int wait_nr(int nr) { int i, ret; for (i = 0; i < nr; i++) { struct io_uring_cqe *cqe; ret = io_uring_wait_cqe(&ring, &cqe); if (ret) return ret; if (cqe->res < 0) { fprintf(stderr, "cqe->res=%d\n", cqe->res); return 1; } io_uring_cqe_seen(&ring, cqe); } return 0; } static unsigned long long mtime_since(const struct timeval *s, const struct timeval *e) { long long sec, usec; sec = e->tv_sec - s->tv_sec; usec = (e->tv_usec - s->tv_usec); if (sec > 0 && usec < 0) { sec--; usec += 1000000; } sec *= 1000; usec /= 1000; return sec + usec; } static unsigned long long mtime_since_now(struct timeval *tv) { struct timeval end; gettimeofday(&end, NULL); return mtime_since(tv, &end); } int main(int argc, char *argv[]) { struct thread_data data; int fd1, fd2, ret, i; struct timeval tv; pthread_t thread; void *tret; ret = io_uring_queue_init(32, &ring, 0); if (ret) { fprintf(stderr, "io_uring_queue_init: %d\n", ret); return 1; } if (create_file(".reuse.1")) { fprintf(stderr, "file creation failed\n"); goto err; } if (create_file(".reuse.2")) { fprintf(stderr, "file creation failed\n"); goto err; } fd1 = open(".reuse.1", O_RDONLY); fd2 = open(".reuse.2", O_RDONLY); data.fd1 = fd1; data.fd2 = fd2; data.do_exit = 0; pthread_create(&thread, NULL, flusher, &data); usleep(10000); gettimeofday(&tv, NULL); for (i = 0; i < 1000; i++) { ret = prep(fd1, str1); if (ret) { fprintf(stderr, "prep1 failed: %d\n", ret); goto err; } ret = prep(fd2, str2); if (ret) { fprintf(stderr, "prep1 failed: %d\n", ret); goto err; } ret = wait_nr(2); if (ret) { fprintf(stderr, "wait_nr: %d\n", ret); goto err; } if (mtime_since_now(&tv) > 5000) break; } data.do_exit = 1; pthread_join(thread, &tret); close(fd2); close(fd1); io_uring_queue_exit(&ring); unlink(".reuse.1"); unlink(".reuse.2"); return 0; err: io_uring_queue_exit(&ring); unlink(".reuse.1"); unlink(".reuse.2"); return 1; }