• 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 
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