brintos

brintos / linux-shallow public Read only

0
0
Text · 8.9 KiB · 7288a05 Raw
424 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * User Events ABI Test Program4 *5 * Copyright (c) 2022 Beau Belgrave <beaub@linux.microsoft.com>6 */7 8#define _GNU_SOURCE9#include <sched.h>10 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>22 23#include "../kselftest_harness.h"24#include "user_events_selftests.h"25 26const char *data_file = "/sys/kernel/tracing/user_events_data";27const char *enable_file = "/sys/kernel/tracing/events/user_events/__abi_event/enable";28const char *multi_dir_glob = "/sys/kernel/tracing/events/user_events_multi/__abi_event.*";29 30static int wait_for_delete(char *dir)31{32	struct stat buf;33	int i;34 35	for (i = 0; i < 10000; ++i) {36		if (stat(dir, &buf) == -1 && errno == ENOENT)37			return 0;38 39		usleep(1000);40	}41 42	return -1;43}44 45static int find_multi_event_dir(char *unique_field, char *out_dir, int dir_len)46{47	char path[256];48	glob_t buf;49	int i, ret;50 51	ret = glob(multi_dir_glob, GLOB_ONLYDIR, NULL, &buf);52 53	if (ret)54		return -1;55 56	ret = -1;57 58	for (i = 0; i < buf.gl_pathc; ++i) {59		FILE *fp;60 61		snprintf(path, sizeof(path), "%s/format", buf.gl_pathv[i]);62		fp = fopen(path, "r");63 64		if (!fp)65			continue;66 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;74			}75		}76 77		fclose(fp);78	}79out:80	globfree(&buf);81 82	return ret;83}84 85static bool event_exists(void)86{87	int fd = open(enable_file, O_RDWR);88 89	if (fd < 0)90		return false;91 92	close(fd);93 94	return true;95}96 97static int change_event(bool enable)98{99	int fd = open(enable_file, O_RDWR);100	int ret;101 102	if (fd < 0)103		return -1;104 105	if (enable)106		ret = write(fd, "1", 1);107	else108		ret = write(fd, "0", 1);109 110	close(fd);111 112	if (ret == 1)113		ret = 0;114	else115		ret = -1;116 117	return ret;118}119 120static int event_delete(void)121{122	int fd = open(data_file, O_RDWR);123	int ret;124 125	if (fd < 0)126		return -1;127 128	ret = ioctl(fd, DIAG_IOCSDEL, "__abi_event");129 130	close(fd);131 132	return ret;133}134 135static int reg_enable_multi(void *enable, int size, int bit, int flags,136			    char *args)137{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;143 144	if (fd < 0)145		return -1;146 147	len = snprintf(full_args, sizeof(full_args), "__abi_event %s", args);148 149	if (len > sizeof(full_args)) {150		ret = -E2BIG;151		goto out;152	}153 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;160 161	ret = ioctl(fd, DIAG_IOCSREG, &reg);162out:163	close(fd);164 165	return ret;166}167 168static int reg_enable_flags(void *enable, int size, int bit, int flags)169{170	struct user_reg reg = {0};171	int fd = open(data_file, O_RDWR);172	int ret;173 174	if (fd < 0)175		return -1;176 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;183 184	ret = ioctl(fd, DIAG_IOCSREG, &reg);185 186	close(fd);187 188	return ret;189}190 191static int reg_enable(void *enable, int size, int bit)192{193	return reg_enable_flags(enable, size, bit, 0);194}195 196static int reg_disable(void *enable, int bit)197{198	struct user_unreg reg = {0};199	int fd = open(data_file, O_RDWR);200	int ret;201 202	if (fd < 0)203		return -1;204 205	reg.size = sizeof(reg);206	reg.disable_bit = bit;207	reg.disable_addr = (__u64)enable;208 209	ret = ioctl(fd, DIAG_IOCSUNREG, &reg);210 211	close(fd);212 213	return ret;214}215 216FIXTURE(user) {217	int check;218	long check_long;219	bool umount;220};221 222FIXTURE_SETUP(user) {223	USER_EVENT_FIXTURE_SETUP(return, self->umount);224 225	change_event(false);226	self->check = 0;227	self->check_long = 0;228}229 230FIXTURE_TEARDOWN(user) {231	USER_EVENT_FIXTURE_TEARDOWN(self->umount);232}233 234TEST_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);242 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);248 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));253}254 255TEST_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));260 261	/* Ensure it exists after close and disable */262	ASSERT_TRUE(event_exists());263 264	/* Ensure we can delete it */265	ASSERT_EQ(0, event_delete());266 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));270 271	/* Ensure it does not exist after invalid flags */272	ASSERT_FALSE(event_exists());273}274 275TEST_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));282 283#if BITS_PER_LONG == 8284	/* 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#endif289 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));299}300 301TEST_F(user, multi_format) {302	char first_dir[256];303	char second_dir[256];304	struct stat buf;305 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"));309 310	ASSERT_EQ(0, reg_enable_multi(&self->check, sizeof(int), 1,311				      0, "u64 multi_second"));312 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"));316 317	ASSERT_EQ(0, find_multi_event_dir("multi_first",318					  first_dir, sizeof(first_dir)));319 320	ASSERT_EQ(0, find_multi_event_dir("multi_second",321					  second_dir, sizeof(second_dir)));322 323	/* Should not be found in the same dir */324	ASSERT_NE(0, strcmp(first_dir, second_dir));325 326	/* First dir should still exist */327	ASSERT_EQ(0, stat(first_dir, &buf));328 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));332 333	/* Second dir should still exist */334	ASSERT_EQ(0, stat(second_dir, &buf));335 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));342}343 344TEST_F(user, forks) {345	int i;346 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);350 351	if (fork() == 0) {352		/* Force COW */353		self->check = 0;354 355		/* Up to 1 sec for enablement */356		for (i = 0; i < 10; ++i) {357			usleep(100000);358 359			if (self->check)360				exit(0);361		}362 363		exit(1);364	}365 366	/* Allow generous time for COW, then enable */367	usleep(100000);368	ASSERT_EQ(0, change_event(true));369 370	ASSERT_NE(-1, wait(&i));371	ASSERT_EQ(0, WEXITSTATUS(i));372 373	/* Ensure child doesn't disable parent */374	if (fork() == 0)375		exit(reg_disable(&self->check, 0));376 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);382}383 384/* Waits up to 1 sec for enablement */385static int clone_check(void *check)386{387	int i;388 389	for (i = 0; i < 10; ++i) {390		usleep(100000);391 392		if (*(int *)check)393			return 0;394	}395 396	return 1;397}398 399TEST_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);404 405	ASSERT_NE(MAP_FAILED, stack);406	ASSERT_EQ(0, reg_enable(&self->check, sizeof(int), 0));407	ASSERT_EQ(0, self->check);408 409	/* Shared VM should see enablements */410	ASSERT_NE(-1, clone(&clone_check, stack + stack_size,411			    CLONE_VM | SIGCHLD, &self->check));412 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));418}419 420int main(int argc, char **argv)421{422	return test_harness_run(argc, argv);423}424