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