1 // SPDX-License-Identifier: GPL-2.0-or-later
2
3 /* Basic per-epoll context busy poll test.
4 *
5 * Only tests the ioctls, but should be expanded to test two connected hosts in
6 * the future
7 */
8
9 #define _GNU_SOURCE
10
11 #include <error.h>
12 #include <errno.h>
13 #include <inttypes.h>
14 #include <limits.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19
20 #include <sys/capability.h>
21
22 #include <sys/epoll.h>
23 #include <sys/ioctl.h>
24 #include <sys/socket.h>
25
26 #include "../kselftest_harness.h"
27
28 /* if the headers haven't been updated, we need to define some things */
29 #if !defined(EPOLL_IOC_TYPE)
30 struct epoll_params {
31 uint32_t busy_poll_usecs;
32 uint16_t busy_poll_budget;
33 uint8_t prefer_busy_poll;
34
35 /* pad the struct to a multiple of 64bits */
36 uint8_t __pad;
37 };
38
39 #define EPOLL_IOC_TYPE 0x8A
40 #define EPIOCSPARAMS _IOW(EPOLL_IOC_TYPE, 0x01, struct epoll_params)
41 #define EPIOCGPARAMS _IOR(EPOLL_IOC_TYPE, 0x02, struct epoll_params)
42 #endif
43
FIXTURE(invalid_fd)44 FIXTURE(invalid_fd)
45 {
46 int invalid_fd;
47 struct epoll_params params;
48 };
49
FIXTURE_SETUP(invalid_fd)50 FIXTURE_SETUP(invalid_fd)
51 {
52 int ret;
53
54 ret = socket(AF_UNIX, SOCK_DGRAM, 0);
55 EXPECT_NE(-1, ret)
56 TH_LOG("error creating unix socket");
57
58 self->invalid_fd = ret;
59 }
60
FIXTURE_TEARDOWN(invalid_fd)61 FIXTURE_TEARDOWN(invalid_fd)
62 {
63 int ret;
64
65 ret = close(self->invalid_fd);
66 EXPECT_EQ(0, ret);
67 }
68
TEST_F(invalid_fd,test_invalid_fd)69 TEST_F(invalid_fd, test_invalid_fd)
70 {
71 int ret;
72
73 ret = ioctl(self->invalid_fd, EPIOCGPARAMS, &self->params);
74
75 EXPECT_EQ(-1, ret)
76 TH_LOG("EPIOCGPARAMS on invalid epoll FD should error");
77
78 EXPECT_EQ(ENOTTY, errno)
79 TH_LOG("EPIOCGPARAMS on invalid epoll FD should set errno to ENOTTY");
80
81 memset(&self->params, 0, sizeof(struct epoll_params));
82
83 ret = ioctl(self->invalid_fd, EPIOCSPARAMS, &self->params);
84
85 EXPECT_EQ(-1, ret)
86 TH_LOG("EPIOCSPARAMS on invalid epoll FD should error");
87
88 EXPECT_EQ(ENOTTY, errno)
89 TH_LOG("EPIOCSPARAMS on invalid epoll FD should set errno to ENOTTY");
90 }
91
FIXTURE(epoll_busy_poll)92 FIXTURE(epoll_busy_poll)
93 {
94 int fd;
95 struct epoll_params params;
96 struct epoll_params *invalid_params;
97 cap_t caps;
98 };
99
FIXTURE_SETUP(epoll_busy_poll)100 FIXTURE_SETUP(epoll_busy_poll)
101 {
102 int ret;
103
104 ret = epoll_create1(0);
105 EXPECT_NE(-1, ret)
106 TH_LOG("epoll_create1 failed?");
107
108 self->fd = ret;
109
110 self->caps = cap_get_proc();
111 EXPECT_NE(NULL, self->caps);
112 }
113
FIXTURE_TEARDOWN(epoll_busy_poll)114 FIXTURE_TEARDOWN(epoll_busy_poll)
115 {
116 int ret;
117
118 ret = close(self->fd);
119 EXPECT_EQ(0, ret);
120
121 ret = cap_free(self->caps);
122 EXPECT_NE(-1, ret)
123 TH_LOG("unable to free capabilities");
124 }
125
TEST_F(epoll_busy_poll,test_get_params)126 TEST_F(epoll_busy_poll, test_get_params)
127 {
128 /* begin by getting the epoll params from the kernel
129 *
130 * the default should be default and all fields should be zero'd by the
131 * kernel, so set params fields to garbage to test this.
132 */
133 int ret = 0;
134
135 self->params.busy_poll_usecs = 0xff;
136 self->params.busy_poll_budget = 0xff;
137 self->params.prefer_busy_poll = 1;
138 self->params.__pad = 0xf;
139
140 ret = ioctl(self->fd, EPIOCGPARAMS, &self->params);
141 EXPECT_EQ(0, ret)
142 TH_LOG("ioctl EPIOCGPARAMS should succeed");
143
144 EXPECT_EQ(0, self->params.busy_poll_usecs)
145 TH_LOG("EPIOCGPARAMS busy_poll_usecs should have been 0");
146
147 EXPECT_EQ(0, self->params.busy_poll_budget)
148 TH_LOG("EPIOCGPARAMS busy_poll_budget should have been 0");
149
150 EXPECT_EQ(0, self->params.prefer_busy_poll)
151 TH_LOG("EPIOCGPARAMS prefer_busy_poll should have been 0");
152
153 EXPECT_EQ(0, self->params.__pad)
154 TH_LOG("EPIOCGPARAMS __pad should have been 0");
155
156 self->invalid_params = (struct epoll_params *)0xdeadbeef;
157 ret = ioctl(self->fd, EPIOCGPARAMS, self->invalid_params);
158
159 EXPECT_EQ(-1, ret)
160 TH_LOG("EPIOCGPARAMS should error with invalid params");
161
162 EXPECT_EQ(EFAULT, errno)
163 TH_LOG("EPIOCGPARAMS with invalid params should set errno to EFAULT");
164 }
165
TEST_F(epoll_busy_poll,test_set_invalid)166 TEST_F(epoll_busy_poll, test_set_invalid)
167 {
168 int ret;
169
170 memset(&self->params, 0, sizeof(struct epoll_params));
171
172 self->params.__pad = 1;
173
174 ret = ioctl(self->fd, EPIOCSPARAMS, &self->params);
175
176 EXPECT_EQ(-1, ret)
177 TH_LOG("EPIOCSPARAMS non-zero __pad should error");
178
179 EXPECT_EQ(EINVAL, errno)
180 TH_LOG("EPIOCSPARAMS non-zero __pad errno should be EINVAL");
181
182 self->params.__pad = 0;
183 self->params.busy_poll_usecs = (uint32_t)INT_MAX + 1;
184
185 ret = ioctl(self->fd, EPIOCSPARAMS, &self->params);
186
187 EXPECT_EQ(-1, ret)
188 TH_LOG("EPIOCSPARAMS should error busy_poll_usecs > S32_MAX");
189
190 EXPECT_EQ(EINVAL, errno)
191 TH_LOG("EPIOCSPARAMS busy_poll_usecs > S32_MAX errno should be EINVAL");
192
193 self->params.__pad = 0;
194 self->params.busy_poll_usecs = 32;
195 self->params.prefer_busy_poll = 2;
196
197 ret = ioctl(self->fd, EPIOCSPARAMS, &self->params);
198
199 EXPECT_EQ(-1, ret)
200 TH_LOG("EPIOCSPARAMS should error prefer_busy_poll > 1");
201
202 EXPECT_EQ(EINVAL, errno)
203 TH_LOG("EPIOCSPARAMS prefer_busy_poll > 1 errno should be EINVAL");
204
205 self->params.__pad = 0;
206 self->params.busy_poll_usecs = 32;
207 self->params.prefer_busy_poll = 1;
208
209 /* set budget well above kernel's NAPI_POLL_WEIGHT of 64 */
210 self->params.busy_poll_budget = UINT16_MAX;
211
212 /* test harness should run with CAP_NET_ADMIN, but let's make sure */
213 cap_flag_value_t tmp;
214
215 ret = cap_get_flag(self->caps, CAP_NET_ADMIN, CAP_EFFECTIVE, &tmp);
216 EXPECT_EQ(0, ret)
217 TH_LOG("unable to get CAP_NET_ADMIN cap flag");
218
219 EXPECT_EQ(CAP_SET, tmp)
220 TH_LOG("expecting CAP_NET_ADMIN to be set for the test harness");
221
222 /* at this point we know CAP_NET_ADMIN is available, so setting the
223 * params with a busy_poll_budget > NAPI_POLL_WEIGHT should succeed
224 */
225 ret = ioctl(self->fd, EPIOCSPARAMS, &self->params);
226
227 EXPECT_EQ(0, ret)
228 TH_LOG("EPIOCSPARAMS should allow busy_poll_budget > NAPI_POLL_WEIGHT");
229
230 /* remove CAP_NET_ADMIN from our effective set */
231 cap_value_t net_admin[] = { CAP_NET_ADMIN };
232
233 ret = cap_set_flag(self->caps, CAP_EFFECTIVE, 1, net_admin, CAP_CLEAR);
234 EXPECT_EQ(0, ret)
235 TH_LOG("couldn't clear CAP_NET_ADMIN");
236
237 ret = cap_set_proc(self->caps);
238 EXPECT_EQ(0, ret)
239 TH_LOG("cap_set_proc should drop CAP_NET_ADMIN");
240
241 /* this is now expected to fail */
242 ret = ioctl(self->fd, EPIOCSPARAMS, &self->params);
243
244 EXPECT_EQ(-1, ret)
245 TH_LOG("EPIOCSPARAMS should error busy_poll_budget > NAPI_POLL_WEIGHT");
246
247 EXPECT_EQ(EPERM, errno)
248 TH_LOG("EPIOCSPARAMS errno should be EPERM busy_poll_budget > NAPI_POLL_WEIGHT");
249
250 /* restore CAP_NET_ADMIN to our effective set */
251 ret = cap_set_flag(self->caps, CAP_EFFECTIVE, 1, net_admin, CAP_SET);
252 EXPECT_EQ(0, ret)
253 TH_LOG("couldn't restore CAP_NET_ADMIN");
254
255 ret = cap_set_proc(self->caps);
256 EXPECT_EQ(0, ret)
257 TH_LOG("cap_set_proc should set CAP_NET_ADMIN");
258
259 self->invalid_params = (struct epoll_params *)0xdeadbeef;
260 ret = ioctl(self->fd, EPIOCSPARAMS, self->invalid_params);
261
262 EXPECT_EQ(-1, ret)
263 TH_LOG("EPIOCSPARAMS should error when epoll_params is invalid");
264
265 EXPECT_EQ(EFAULT, errno)
266 TH_LOG("EPIOCSPARAMS should set errno to EFAULT when epoll_params is invalid");
267 }
268
TEST_F(epoll_busy_poll,test_set_and_get_valid)269 TEST_F(epoll_busy_poll, test_set_and_get_valid)
270 {
271 int ret;
272
273 memset(&self->params, 0, sizeof(struct epoll_params));
274
275 self->params.busy_poll_usecs = 25;
276 self->params.busy_poll_budget = 16;
277 self->params.prefer_busy_poll = 1;
278
279 ret = ioctl(self->fd, EPIOCSPARAMS, &self->params);
280
281 EXPECT_EQ(0, ret)
282 TH_LOG("EPIOCSPARAMS with valid params should not error");
283
284 /* check that the kernel returns the same values back */
285
286 memset(&self->params, 0, sizeof(struct epoll_params));
287
288 ret = ioctl(self->fd, EPIOCGPARAMS, &self->params);
289
290 EXPECT_EQ(0, ret)
291 TH_LOG("EPIOCGPARAMS should not error");
292
293 EXPECT_EQ(25, self->params.busy_poll_usecs)
294 TH_LOG("params.busy_poll_usecs incorrect");
295
296 EXPECT_EQ(16, self->params.busy_poll_budget)
297 TH_LOG("params.busy_poll_budget incorrect");
298
299 EXPECT_EQ(1, self->params.prefer_busy_poll)
300 TH_LOG("params.prefer_busy_poll incorrect");
301
302 EXPECT_EQ(0, self->params.__pad)
303 TH_LOG("params.__pad was not 0");
304 }
305
TEST_F(epoll_busy_poll,test_invalid_ioctl)306 TEST_F(epoll_busy_poll, test_invalid_ioctl)
307 {
308 int invalid_ioctl = EPIOCGPARAMS + 10;
309 int ret;
310
311 ret = ioctl(self->fd, invalid_ioctl, &self->params);
312
313 EXPECT_EQ(-1, ret)
314 TH_LOG("invalid ioctl should return error");
315
316 EXPECT_EQ(EINVAL, errno)
317 TH_LOG("invalid ioctl should set errno to EINVAL");
318 }
319
320 TEST_HARNESS_MAIN
321