drm/atomic-helper: document drm_atomic_helper_check() restrictions
[drm/drm-misc.git] / tools / testing / selftests / user_events / abi_test.c
blob7288a05136ba848edf8269cf0d65cfd3d9365041
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * User Events ABI Test Program
5 * Copyright (c) 2022 Beau Belgrave <beaub@linux.microsoft.com>
6 */
8 #define _GNU_SOURCE
9 #include <sched.h>
11 #include <errno.h>
12 #include <linux/user_events.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <fcntl.h>
16 #include <sys/ioctl.h>
17 #include <sys/stat.h>
18 #include <unistd.h>
19 #include <glob.h>
20 #include <string.h>
21 #include <asm/unistd.h>
23 #include "../kselftest_harness.h"
24 #include "user_events_selftests.h"
26 const char *data_file = "/sys/kernel/tracing/user_events_data";
27 const char *enable_file = "/sys/kernel/tracing/events/user_events/__abi_event/enable";
28 const char *multi_dir_glob = "/sys/kernel/tracing/events/user_events_multi/__abi_event.*";
30 static int wait_for_delete(char *dir)
32 struct stat buf;
33 int i;
35 for (i = 0; i < 10000; ++i) {
36 if (stat(dir, &buf) == -1 && errno == ENOENT)
37 return 0;
39 usleep(1000);
42 return -1;
45 static int find_multi_event_dir(char *unique_field, char *out_dir, int dir_len)
47 char path[256];
48 glob_t buf;
49 int i, ret;
51 ret = glob(multi_dir_glob, GLOB_ONLYDIR, NULL, &buf);
53 if (ret)
54 return -1;
56 ret = -1;
58 for (i = 0; i < buf.gl_pathc; ++i) {
59 FILE *fp;
61 snprintf(path, sizeof(path), "%s/format", buf.gl_pathv[i]);
62 fp = fopen(path, "r");
64 if (!fp)
65 continue;
67 while (fgets(path, sizeof(path), fp) != NULL) {
68 if (strstr(path, unique_field)) {
69 fclose(fp);
70 /* strscpy is not available, use snprintf */
71 snprintf(out_dir, dir_len, "%s", buf.gl_pathv[i]);
72 ret = 0;
73 goto out;
77 fclose(fp);
79 out:
80 globfree(&buf);
82 return ret;
85 static bool event_exists(void)
87 int fd = open(enable_file, O_RDWR);
89 if (fd < 0)
90 return false;
92 close(fd);
94 return true;
97 static int change_event(bool enable)
99 int fd = open(enable_file, O_RDWR);
100 int ret;
102 if (fd < 0)
103 return -1;
105 if (enable)
106 ret = write(fd, "1", 1);
107 else
108 ret = write(fd, "0", 1);
110 close(fd);
112 if (ret == 1)
113 ret = 0;
114 else
115 ret = -1;
117 return ret;
120 static int event_delete(void)
122 int fd = open(data_file, O_RDWR);
123 int ret;
125 if (fd < 0)
126 return -1;
128 ret = ioctl(fd, DIAG_IOCSDEL, "__abi_event");
130 close(fd);
132 return ret;
135 static int reg_enable_multi(void *enable, int size, int bit, int flags,
136 char *args)
138 struct user_reg reg = {0};
139 char full_args[512] = {0};
140 int fd = open(data_file, O_RDWR);
141 int len;
142 int ret;
144 if (fd < 0)
145 return -1;
147 len = snprintf(full_args, sizeof(full_args), "__abi_event %s", args);
149 if (len > sizeof(full_args)) {
150 ret = -E2BIG;
151 goto out;
154 reg.size = sizeof(reg);
155 reg.name_args = (__u64)full_args;
156 reg.flags = USER_EVENT_REG_MULTI_FORMAT | flags;
157 reg.enable_bit = bit;
158 reg.enable_addr = (__u64)enable;
159 reg.enable_size = size;
161 ret = ioctl(fd, DIAG_IOCSREG, &reg);
162 out:
163 close(fd);
165 return ret;
168 static int reg_enable_flags(void *enable, int size, int bit, int flags)
170 struct user_reg reg = {0};
171 int fd = open(data_file, O_RDWR);
172 int ret;
174 if (fd < 0)
175 return -1;
177 reg.size = sizeof(reg);
178 reg.name_args = (__u64)"__abi_event";
179 reg.flags = flags;
180 reg.enable_bit = bit;
181 reg.enable_addr = (__u64)enable;
182 reg.enable_size = size;
184 ret = ioctl(fd, DIAG_IOCSREG, &reg);
186 close(fd);
188 return ret;
191 static int reg_enable(void *enable, int size, int bit)
193 return reg_enable_flags(enable, size, bit, 0);
196 static int reg_disable(void *enable, int bit)
198 struct user_unreg reg = {0};
199 int fd = open(data_file, O_RDWR);
200 int ret;
202 if (fd < 0)
203 return -1;
205 reg.size = sizeof(reg);
206 reg.disable_bit = bit;
207 reg.disable_addr = (__u64)enable;
209 ret = ioctl(fd, DIAG_IOCSUNREG, &reg);
211 close(fd);
213 return ret;
216 FIXTURE(user) {
217 int check;
218 long check_long;
219 bool umount;
222 FIXTURE_SETUP(user) {
223 USER_EVENT_FIXTURE_SETUP(return, self->umount);
225 change_event(false);
226 self->check = 0;
227 self->check_long = 0;
230 FIXTURE_TEARDOWN(user) {
231 USER_EVENT_FIXTURE_TEARDOWN(self->umount);
234 TEST_F(user, enablement) {
235 /* Changes should be reflected immediately */
236 ASSERT_EQ(0, self->check);
237 ASSERT_EQ(0, reg_enable(&self->check, sizeof(int), 0));
238 ASSERT_EQ(0, change_event(true));
239 ASSERT_EQ(1, self->check);
240 ASSERT_EQ(0, change_event(false));
241 ASSERT_EQ(0, self->check);
243 /* Ensure kernel clears bit after disable */
244 ASSERT_EQ(0, change_event(true));
245 ASSERT_EQ(1, self->check);
246 ASSERT_EQ(0, reg_disable(&self->check, 0));
247 ASSERT_EQ(0, self->check);
249 /* Ensure doesn't change after unreg */
250 ASSERT_EQ(0, change_event(true));
251 ASSERT_EQ(0, self->check);
252 ASSERT_EQ(0, change_event(false));
255 TEST_F(user, flags) {
256 /* USER_EVENT_REG_PERSIST is allowed */
257 ASSERT_EQ(0, reg_enable_flags(&self->check, sizeof(int), 0,
258 USER_EVENT_REG_PERSIST));
259 ASSERT_EQ(0, reg_disable(&self->check, 0));
261 /* Ensure it exists after close and disable */
262 ASSERT_TRUE(event_exists());
264 /* Ensure we can delete it */
265 ASSERT_EQ(0, event_delete());
267 /* USER_EVENT_REG_MAX or above is not allowed */
268 ASSERT_EQ(-1, reg_enable_flags(&self->check, sizeof(int), 0,
269 USER_EVENT_REG_MAX));
271 /* Ensure it does not exist after invalid flags */
272 ASSERT_FALSE(event_exists());
275 TEST_F(user, bit_sizes) {
276 /* Allow 0-31 bits for 32-bit */
277 ASSERT_EQ(0, reg_enable(&self->check, sizeof(int), 0));
278 ASSERT_EQ(0, reg_enable(&self->check, sizeof(int), 31));
279 ASSERT_NE(0, reg_enable(&self->check, sizeof(int), 32));
280 ASSERT_EQ(0, reg_disable(&self->check, 0));
281 ASSERT_EQ(0, reg_disable(&self->check, 31));
283 #if BITS_PER_LONG == 8
284 /* Allow 0-64 bits for 64-bit */
285 ASSERT_EQ(0, reg_enable(&self->check_long, sizeof(long), 63));
286 ASSERT_NE(0, reg_enable(&self->check_long, sizeof(long), 64));
287 ASSERT_EQ(0, reg_disable(&self->check_long, 63));
288 #endif
290 /* Disallowed sizes (everything beside 4 and 8) */
291 ASSERT_NE(0, reg_enable(&self->check, 1, 0));
292 ASSERT_NE(0, reg_enable(&self->check, 2, 0));
293 ASSERT_NE(0, reg_enable(&self->check, 3, 0));
294 ASSERT_NE(0, reg_enable(&self->check, 5, 0));
295 ASSERT_NE(0, reg_enable(&self->check, 6, 0));
296 ASSERT_NE(0, reg_enable(&self->check, 7, 0));
297 ASSERT_NE(0, reg_enable(&self->check, 9, 0));
298 ASSERT_NE(0, reg_enable(&self->check, 128, 0));
301 TEST_F(user, multi_format) {
302 char first_dir[256];
303 char second_dir[256];
304 struct stat buf;
306 /* Multiple formats for the same name should work */
307 ASSERT_EQ(0, reg_enable_multi(&self->check, sizeof(int), 0,
308 0, "u32 multi_first"));
310 ASSERT_EQ(0, reg_enable_multi(&self->check, sizeof(int), 1,
311 0, "u64 multi_second"));
313 /* Same name with same format should also work */
314 ASSERT_EQ(0, reg_enable_multi(&self->check, sizeof(int), 2,
315 0, "u64 multi_second"));
317 ASSERT_EQ(0, find_multi_event_dir("multi_first",
318 first_dir, sizeof(first_dir)));
320 ASSERT_EQ(0, find_multi_event_dir("multi_second",
321 second_dir, sizeof(second_dir)));
323 /* Should not be found in the same dir */
324 ASSERT_NE(0, strcmp(first_dir, second_dir));
326 /* First dir should still exist */
327 ASSERT_EQ(0, stat(first_dir, &buf));
329 /* Disabling first register should remove first dir */
330 ASSERT_EQ(0, reg_disable(&self->check, 0));
331 ASSERT_EQ(0, wait_for_delete(first_dir));
333 /* Second dir should still exist */
334 ASSERT_EQ(0, stat(second_dir, &buf));
336 /* Disabling second register should remove second dir */
337 ASSERT_EQ(0, reg_disable(&self->check, 1));
338 /* Ensure bit 1 and 2 are tied together, should not delete yet */
339 ASSERT_EQ(0, stat(second_dir, &buf));
340 ASSERT_EQ(0, reg_disable(&self->check, 2));
341 ASSERT_EQ(0, wait_for_delete(second_dir));
344 TEST_F(user, forks) {
345 int i;
347 /* Ensure COW pages get updated after fork */
348 ASSERT_EQ(0, reg_enable(&self->check, sizeof(int), 0));
349 ASSERT_EQ(0, self->check);
351 if (fork() == 0) {
352 /* Force COW */
353 self->check = 0;
355 /* Up to 1 sec for enablement */
356 for (i = 0; i < 10; ++i) {
357 usleep(100000);
359 if (self->check)
360 exit(0);
363 exit(1);
366 /* Allow generous time for COW, then enable */
367 usleep(100000);
368 ASSERT_EQ(0, change_event(true));
370 ASSERT_NE(-1, wait(&i));
371 ASSERT_EQ(0, WEXITSTATUS(i));
373 /* Ensure child doesn't disable parent */
374 if (fork() == 0)
375 exit(reg_disable(&self->check, 0));
377 ASSERT_NE(-1, wait(&i));
378 ASSERT_EQ(0, WEXITSTATUS(i));
379 ASSERT_EQ(1, self->check);
380 ASSERT_EQ(0, change_event(false));
381 ASSERT_EQ(0, self->check);
384 /* Waits up to 1 sec for enablement */
385 static int clone_check(void *check)
387 int i;
389 for (i = 0; i < 10; ++i) {
390 usleep(100000);
392 if (*(int *)check)
393 return 0;
396 return 1;
399 TEST_F(user, clones) {
400 int i, stack_size = 4096;
401 void *stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE,
402 MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK,
403 -1, 0);
405 ASSERT_NE(MAP_FAILED, stack);
406 ASSERT_EQ(0, reg_enable(&self->check, sizeof(int), 0));
407 ASSERT_EQ(0, self->check);
409 /* Shared VM should see enablements */
410 ASSERT_NE(-1, clone(&clone_check, stack + stack_size,
411 CLONE_VM | SIGCHLD, &self->check));
413 ASSERT_EQ(0, change_event(true));
414 ASSERT_NE(-1, wait(&i));
415 ASSERT_EQ(0, WEXITSTATUS(i));
416 munmap(stack, stack_size);
417 ASSERT_EQ(0, change_event(false));
420 int main(int argc, char **argv)
422 return test_harness_run(argc, argv);