• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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