1 /* SPDX-License-Identifier: MIT */
2 /*
3 * Description: read /proc/kallsyms. Mostly just here so that fops->read() can
4 * get exercised, with and without registered buffers
5 */
6 #include <errno.h>
7 #include <stdio.h>
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <fcntl.h>
12 #include <sys/types.h>
13 #include <poll.h>
14 #include <sys/eventfd.h>
15 #include <sys/resource.h>
16
17 #include "helpers.h"
18 #include "liburing.h"
19
20 #define FILE_SIZE (8 * 1024)
21 #define BS 8192
22 #define BUFFERS (FILE_SIZE / BS)
23
24 static struct iovec *vecs;
25 static int warned;
26
__test_io(const char * file,struct io_uring * ring,int fixed,int nonvec)27 static int __test_io(const char *file, struct io_uring *ring, int fixed, int nonvec)
28 {
29 struct io_uring_sqe *sqe;
30 struct io_uring_cqe *cqe;
31 int open_flags;
32 int i, fd = -1, ret;
33 off_t offset;
34
35 open_flags = O_RDONLY;
36 if (fixed) {
37 ret = t_register_buffers(ring, vecs, BUFFERS);
38 if (ret == T_SETUP_SKIP)
39 return 0;
40 if (ret != T_SETUP_OK) {
41 fprintf(stderr, "buffer reg failed: %d\n", ret);
42 goto err;
43 }
44 }
45
46 fd = open(file, open_flags);
47 if (fd < 0) {
48 if (errno == EINVAL || errno == EPERM || errno == ENOENT)
49 return 0;
50 perror("file open");
51 goto err;
52 }
53
54 offset = 0;
55 for (i = 0; i < BUFFERS; i++) {
56 int do_fixed = fixed;
57
58 sqe = io_uring_get_sqe(ring);
59 if (!sqe) {
60 fprintf(stderr, "sqe get failed\n");
61 goto err;
62 }
63 if (fixed && (i & 1))
64 do_fixed = 0;
65 if (do_fixed) {
66 io_uring_prep_read_fixed(sqe, fd, vecs[i].iov_base,
67 vecs[i].iov_len, offset, i);
68 } else if (nonvec) {
69 io_uring_prep_read(sqe, fd, vecs[i].iov_base,
70 vecs[i].iov_len, offset);
71 } else {
72 io_uring_prep_readv(sqe, fd, &vecs[i], 1, offset);
73 }
74 sqe->user_data = i;
75 offset += BS;
76 }
77
78 ret = io_uring_submit(ring);
79 if (ret != BUFFERS) {
80 fprintf(stderr, "submit got %d, wanted %d\n", ret, BUFFERS);
81 goto err;
82 }
83
84 for (i = 0; i < BUFFERS; i++) {
85 ret = io_uring_wait_cqe(ring, &cqe);
86 if (ret) {
87 fprintf(stderr, "wait_cqe=%d\n", ret);
88 goto err;
89 }
90 if (cqe->res == -EINVAL && nonvec) {
91 if (!warned) {
92 fprintf(stdout, "Non-vectored IO not "
93 "supported, skipping\n");
94 warned = 1;
95 }
96 }
97 io_uring_cqe_seen(ring, cqe);
98 }
99
100 if (fixed) {
101 ret = io_uring_unregister_buffers(ring);
102 if (ret) {
103 fprintf(stderr, "buffer unreg failed: %d\n", ret);
104 goto err;
105 }
106 }
107
108 close(fd);
109 return 0;
110 err:
111 if (fd != -1)
112 close(fd);
113 return 1;
114 }
test_io(const char * file,int fixed,int nonvec)115 static int test_io(const char *file, int fixed, int nonvec)
116 {
117 struct io_uring ring;
118 int ret, ring_flags = 0;
119
120 ret = t_create_ring(64, &ring, ring_flags);
121 if (ret == T_SETUP_SKIP)
122 return 0;
123 if (ret != T_SETUP_OK) {
124 fprintf(stderr, "ring create failed: %d\n", ret);
125 return 1;
126 }
127
128 ret = __test_io(file, &ring, fixed, nonvec);
129 io_uring_queue_exit(&ring);
130 return ret;
131 }
132
has_nonvec_read(void)133 static int has_nonvec_read(void)
134 {
135 struct io_uring_probe *p;
136 struct io_uring ring;
137 int ret;
138
139 ret = io_uring_queue_init(1, &ring, 0);
140 if (ret) {
141 fprintf(stderr, "queue init failed: %d\n", ret);
142 exit(ret);
143 }
144
145 p = t_calloc(1, sizeof(*p) + 256 * sizeof(struct io_uring_probe_op));
146 ret = io_uring_register_probe(&ring, p, 256);
147 /* if we don't have PROBE_REGISTER, we don't have OP_READ/WRITE */
148 if (ret == -EINVAL) {
149 out:
150 io_uring_queue_exit(&ring);
151 free(p);
152 return 0;
153 } else if (ret) {
154 fprintf(stderr, "register_probe: %d\n", ret);
155 goto out;
156 }
157
158 if (p->ops_len <= IORING_OP_READ)
159 goto out;
160 if (!(p->ops[IORING_OP_READ].flags & IO_URING_OP_SUPPORTED))
161 goto out;
162 io_uring_queue_exit(&ring);
163 free(p);
164 return 1;
165 }
166
main(int argc,char * argv[])167 int main(int argc, char *argv[])
168 {
169 int ret, nonvec;
170
171 if (argc > 1)
172 return T_EXIT_SKIP;
173
174 vecs = t_create_buffers(BUFFERS, BS);
175
176 /* if we don't have nonvec read, skip testing that */
177 nonvec = has_nonvec_read();
178
179 if (nonvec) {
180 ret = test_io("/proc/kallsyms", 0, 0);
181 if (ret)
182 goto err;
183 }
184
185 ret = test_io("/proc/kallsyms", 0, 1);
186 if (ret)
187 goto err;
188
189 if (nonvec) {
190 ret = test_io("/proc/kallsyms", 1, 0);
191 if (ret)
192 goto err;
193 }
194
195 ret = test_io("/proc/kallsyms", 1, 1);
196 if (ret)
197 goto err;
198
199 return 0;
200 err:
201 fprintf(stderr, "Reading kallsyms failed\n");
202 return 1;
203 }
204