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