1 /* SPDX-License-Identifier: MIT */
2 /*
3 * Description: basic fadvise test
4 */
5 #include <errno.h>
6 #include <stdio.h>
7 #include <unistd.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <fcntl.h>
11 #include <sys/types.h>
12 #include <sys/time.h>
13
14 #include "helpers.h"
15 #include "liburing.h"
16
17 #define FILE_SIZE (128 * 1024)
18 #define LOOPS 100
19 #define MIN_LOOPS 10
20
do_fadvise(struct io_uring * ring,int fd,off_t offset,off_t len,int advice)21 static int do_fadvise(struct io_uring *ring, int fd, off_t offset, off_t len,
22 int advice)
23 {
24 struct io_uring_sqe *sqe;
25 struct io_uring_cqe *cqe;
26 int ret;
27
28 sqe = io_uring_get_sqe(ring);
29 if (!sqe) {
30 fprintf(stderr, "failed to get sqe\n");
31 return 1;
32 }
33
34 io_uring_prep_fadvise(sqe, fd, offset, len, advice);
35 sqe->user_data = advice;
36 ret = io_uring_submit_and_wait(ring, 1);
37 if (ret != 1) {
38 fprintf(stderr, "submit: %d\n", ret);
39 return 1;
40 }
41
42 ret = io_uring_wait_cqe(ring, &cqe);
43 if (ret) {
44 fprintf(stderr, "wait: %d\n", ret);
45 return 1;
46 }
47
48 ret = cqe->res;
49 if (ret == -EINVAL || ret == -EBADF) {
50 fprintf(stdout, "Fadvise not supported, skipping\n");
51 unlink(".fadvise.tmp");
52 exit(T_EXIT_SKIP);
53 } else if (ret) {
54 fprintf(stderr, "cqe->res=%d\n", cqe->res);
55 }
56 io_uring_cqe_seen(ring, cqe);
57 return ret;
58 }
59
do_read(int fd,char * buf)60 static long do_read(int fd, char *buf)
61 {
62 struct timeval tv;
63 int ret;
64 long t;
65
66 ret = lseek(fd, 0, SEEK_SET);
67 if (ret) {
68 perror("lseek");
69 return -1;
70 }
71
72 gettimeofday(&tv, NULL);
73 ret = read(fd, buf, FILE_SIZE);
74 t = utime_since_now(&tv);
75 if (ret < 0) {
76 perror("read");
77 return -1;
78 } else if (ret != FILE_SIZE) {
79 fprintf(stderr, "short read1: %d\n", ret);
80 return -1;
81 }
82
83 return t;
84 }
85
test_fadvise(struct io_uring * ring,const char * filename)86 static int test_fadvise(struct io_uring *ring, const char *filename)
87 {
88 unsigned long cached_read, uncached_read, cached_read2;
89 int fd, ret;
90 char *buf;
91
92 fd = open(filename, O_RDONLY);
93 if (fd < 0) {
94 if (errno == EPERM || errno == EACCES)
95 return T_EXIT_SKIP;
96 perror("open");
97 return 1;
98 }
99
100 buf = t_malloc(FILE_SIZE);
101
102 cached_read = do_read(fd, buf);
103 if (cached_read == -1)
104 return 1;
105
106 ret = do_fadvise(ring, fd, 0, FILE_SIZE, POSIX_FADV_DONTNEED);
107 if (ret)
108 return 1;
109
110 uncached_read = do_read(fd, buf);
111 if (uncached_read == -1)
112 return 1;
113
114 ret = do_fadvise(ring, fd, 0, FILE_SIZE, POSIX_FADV_DONTNEED);
115 if (ret)
116 return 1;
117
118 ret = do_fadvise(ring, fd, 0, FILE_SIZE, POSIX_FADV_WILLNEED);
119 if (ret)
120 return 1;
121
122 fsync(fd);
123
124 cached_read2 = do_read(fd, buf);
125 if (cached_read2 == -1)
126 return 1;
127
128 if (cached_read < uncached_read &&
129 cached_read2 < uncached_read) {
130 free(buf);
131 return 0;
132 }
133
134 free(buf);
135 return 2;
136 }
137
main(int argc,char * argv[])138 int main(int argc, char *argv[])
139 {
140 struct io_uring ring;
141 int ret, i, good, bad;
142 char *fname;
143
144 if (argc > 1) {
145 fname = argv[1];
146 } else {
147 fname = ".fadvise.tmp";
148 t_create_file(fname, FILE_SIZE);
149 }
150 if (io_uring_queue_init(8, &ring, 0)) {
151 fprintf(stderr, "ring creation failed\n");
152 goto err;
153 }
154
155 good = bad = 0;
156 for (i = 0; i < LOOPS; i++) {
157 ret = test_fadvise(&ring, fname);
158 if (ret == T_EXIT_SKIP)
159 return T_EXIT_SKIP;
160 if (ret == 1) {
161 fprintf(stderr, "read_fadvise failed\n");
162 goto err;
163 } else if (!ret)
164 good++;
165 else if (ret == 2)
166 bad++;
167 if (i >= MIN_LOOPS && !bad)
168 break;
169 }
170
171 /* too hard to reliably test, just ignore */
172 if ((0) && bad > good) {
173 fprintf(stderr, "Suspicious timings\n");
174 goto err;
175 }
176
177 if (fname != argv[1])
178 unlink(fname);
179 io_uring_queue_exit(&ring);
180 return T_EXIT_PASS;
181 err:
182 if (fname != argv[1])
183 unlink(fname);
184 return T_EXIT_FAIL;
185 }
186