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
utime_since(const struct timeval * s,const struct timeval * e)21 static unsigned long long utime_since(const struct timeval *s,
22 const struct timeval *e)
23 {
24 long long sec, usec;
25
26 sec = e->tv_sec - s->tv_sec;
27 usec = (e->tv_usec - s->tv_usec);
28 if (sec > 0 && usec < 0) {
29 sec--;
30 usec += 1000000;
31 }
32
33 sec *= 1000000;
34 return sec + usec;
35 }
36
utime_since_now(struct timeval * tv)37 static unsigned long long utime_since_now(struct timeval *tv)
38 {
39 struct timeval end;
40
41 gettimeofday(&end, NULL);
42 return utime_since(tv, &end);
43 }
44
do_fadvise(struct io_uring * ring,int fd,off_t offset,off_t len,int advice)45 static int do_fadvise(struct io_uring *ring, int fd, off_t offset, off_t len,
46 int advice)
47 {
48 struct io_uring_sqe *sqe;
49 struct io_uring_cqe *cqe;
50 int ret;
51
52 sqe = io_uring_get_sqe(ring);
53 if (!sqe) {
54 fprintf(stderr, "failed to get sqe\n");
55 return 1;
56 }
57
58 io_uring_prep_fadvise(sqe, fd, offset, len, advice);
59 sqe->user_data = advice;
60 ret = io_uring_submit_and_wait(ring, 1);
61 if (ret != 1) {
62 fprintf(stderr, "submit: %d\n", ret);
63 return 1;
64 }
65
66 ret = io_uring_wait_cqe(ring, &cqe);
67 if (ret) {
68 fprintf(stderr, "wait: %d\n", ret);
69 return 1;
70 }
71
72 ret = cqe->res;
73 if (ret == -EINVAL || ret == -EBADF) {
74 fprintf(stdout, "Fadvise not supported, skipping\n");
75 unlink(".fadvise.tmp");
76 exit(0);
77 } else if (ret) {
78 fprintf(stderr, "cqe->res=%d\n", cqe->res);
79 }
80 io_uring_cqe_seen(ring, cqe);
81 return ret;
82 }
83
do_read(int fd,char * buf)84 static long do_read(int fd, char *buf)
85 {
86 struct timeval tv;
87 int ret;
88 long t;
89
90 ret = lseek(fd, 0, SEEK_SET);
91 if (ret) {
92 perror("lseek");
93 return -1;
94 }
95
96 gettimeofday(&tv, NULL);
97 ret = read(fd, buf, FILE_SIZE);
98 t = utime_since_now(&tv);
99 if (ret < 0) {
100 perror("read");
101 return -1;
102 } else if (ret != FILE_SIZE) {
103 fprintf(stderr, "short read1: %d\n", ret);
104 return -1;
105 }
106
107 return t;
108 }
109
test_fadvise(struct io_uring * ring,const char * filename)110 static int test_fadvise(struct io_uring *ring, const char *filename)
111 {
112 unsigned long cached_read, uncached_read, cached_read2;
113 int fd, ret;
114 char *buf;
115
116 fd = open(filename, O_RDONLY);
117 if (fd < 0) {
118 perror("open");
119 return 1;
120 }
121
122 buf = t_malloc(FILE_SIZE);
123
124 cached_read = do_read(fd, buf);
125 if (cached_read == -1)
126 return 1;
127
128 ret = do_fadvise(ring, fd, 0, FILE_SIZE, POSIX_FADV_DONTNEED);
129 if (ret)
130 return 1;
131
132 uncached_read = do_read(fd, buf);
133 if (uncached_read == -1)
134 return 1;
135
136 ret = do_fadvise(ring, fd, 0, FILE_SIZE, POSIX_FADV_DONTNEED);
137 if (ret)
138 return 1;
139
140 ret = do_fadvise(ring, fd, 0, FILE_SIZE, POSIX_FADV_WILLNEED);
141 if (ret)
142 return 1;
143
144 fsync(fd);
145
146 cached_read2 = do_read(fd, buf);
147 if (cached_read2 == -1)
148 return 1;
149
150 if (cached_read < uncached_read &&
151 cached_read2 < uncached_read)
152 return 0;
153
154 return 2;
155 }
156
main(int argc,char * argv[])157 int main(int argc, char *argv[])
158 {
159 struct io_uring ring;
160 int ret, i, good, bad;
161 char *fname;
162
163 if (argc > 1) {
164 fname = argv[1];
165 } else {
166 fname = ".fadvise.tmp";
167 t_create_file(fname, FILE_SIZE);
168 }
169 if (io_uring_queue_init(8, &ring, 0)) {
170 fprintf(stderr, "ring creation failed\n");
171 goto err;
172 }
173
174 good = bad = 0;
175 for (i = 0; i < LOOPS; i++) {
176 ret = test_fadvise(&ring, fname);
177 if (ret == 1) {
178 fprintf(stderr, "read_fadvise failed\n");
179 goto err;
180 } else if (!ret)
181 good++;
182 else if (ret == 2)
183 bad++;
184 if (i >= MIN_LOOPS && !bad)
185 break;
186 }
187 if (bad > good) {
188 fprintf(stderr, "Suspicious timings\n");
189 goto err;
190 }
191
192 if (fname != argv[1])
193 unlink(fname);
194 io_uring_queue_exit(&ring);
195 return 0;
196 err:
197 if (fname != argv[1])
198 unlink(fname);
199 return 1;
200 }
201