Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.9.
  1// SPDX-License-Identifier: GPL-2.0
  2#define _GNU_SOURCE
  3#define __SANE_USERSPACE_TYPES__
  4
  5#include <fcntl.h>
  6#include <limits.h>
  7#include <pthread.h>
  8#include <sched.h>
  9#include <stdio.h>
 10#include <string.h>
 11#include <sys/stat.h>
 12#include <sys/mount.h>
 13#include <unistd.h>
 14
 15#include "../kselftest_harness.h"
 16
 17#include <linux/types.h>
 18#include <linux/mount.h>
 19#include <sys/syscall.h>
 20
 21static inline int sys_fsopen(const char *fsname, unsigned int flags)
 22{
 23	return syscall(__NR_fsopen, fsname, flags);
 24}
 25
 26static inline int sys_fsconfig(int fd, unsigned int cmd, const char *key,
 27			       const char *value, int aux)
 28{
 29	return syscall(__NR_fsconfig, fd, cmd, key, value, aux);
 30}
 31
 32static inline int sys_fsmount(int fd, unsigned int flags,
 33			      unsigned int attr_flags)
 34{
 35	return syscall(__NR_fsmount, fd, flags, attr_flags);
 36}
 37
 38#ifndef MOVE_MOUNT_F_EMPTY_PATH
 39#define MOVE_MOUNT_F_EMPTY_PATH 0x00000004 /* Empty from path permitted */
 40#endif
 41
 42static inline int sys_move_mount(int from_dfd, const char *from_pathname,
 43				 int to_dfd, const char *to_pathname,
 44				 unsigned int flags)
 45{
 46	return syscall(__NR_move_mount, from_dfd, from_pathname, to_dfd,
 47		       to_pathname, flags);
 48}
 49
 50FIXTURE(file_stressor) {
 51	int fd_tmpfs;
 52	int nr_procs;
 53	int max_fds;
 54	pid_t *pids_openers;
 55	pid_t *pids_getdents;
 56	int *fd_proc_pid;
 57};
 58
 59FIXTURE_SETUP(file_stressor)
 60{
 61	int fd_context;
 62
 63	ASSERT_EQ(unshare(CLONE_NEWNS), 0);
 64	ASSERT_EQ(mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL), 0);
 65	ASSERT_EQ(mkdir("/slab_typesafe_by_rcu", 0755), 0);
 66
 67	fd_context = sys_fsopen("tmpfs", 0);
 68	ASSERT_GE(fd_context, 0);
 69
 70	ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_CMD_CREATE, NULL, NULL, 0), 0);
 71	self->fd_tmpfs = sys_fsmount(fd_context, 0, 0);
 72	ASSERT_GE(self->fd_tmpfs, 0);
 73	ASSERT_EQ(close(fd_context), 0);
 74
 75	ASSERT_EQ(sys_move_mount(self->fd_tmpfs, "", -EBADF, "/slab_typesafe_by_rcu", MOVE_MOUNT_F_EMPTY_PATH), 0);
 76
 77	self->nr_procs = sysconf(_SC_NPROCESSORS_ONLN);
 78	self->pids_openers = malloc(sizeof(pid_t) * self->nr_procs);
 79	ASSERT_NE(self->pids_openers, NULL);
 80	self->pids_getdents = malloc(sizeof(pid_t) * self->nr_procs);
 81	ASSERT_NE(self->pids_getdents, NULL);
 82	self->fd_proc_pid = malloc(sizeof(int) * self->nr_procs);
 83	ASSERT_NE(self->fd_proc_pid, NULL);
 84	self->max_fds = 500;
 85}
 86
 87FIXTURE_TEARDOWN(file_stressor)
 88{
 89	for (int i = 0; i < self->nr_procs; i++) {
 90		int wstatus;
 91		pid_t pid;
 92
 93		pid = waitpid(self->pids_openers[i], &wstatus, 0);
 94		ASSERT_EQ(pid, self->pids_openers[i]);
 95		ASSERT_TRUE(!WIFEXITED(wstatus) || !WIFSIGNALED(wstatus));
 96
 97		pid = waitpid(self->pids_getdents[i], &wstatus, 0);
 98		ASSERT_EQ(pid, self->pids_getdents[i]);
 99		ASSERT_TRUE(!WIFEXITED(wstatus) || !WIFSIGNALED(wstatus));
100	}
101	free(self->pids_openers);
102	free(self->pids_getdents);
103	ASSERT_EQ(close(self->fd_tmpfs), 0);
104
105	umount2("/slab_typesafe_by_rcu", 0);
106	ASSERT_EQ(rmdir("/slab_typesafe_by_rcu"), 0);
107}
108
109TEST_F_TIMEOUT(file_stressor, slab_typesafe_by_rcu, 900 * 2)
110{
111	for (int i = 0; i < self->nr_procs; i++) {
112		pid_t pid_self;
113
114		self->pids_openers[i] = fork();
115		ASSERT_GE(self->pids_openers[i], 0);
116
117		if (self->pids_openers[i] != 0)
118			continue;
119
120		self->pids_openers[i] = getpid();
121		for (;;) {
122			for (int i = 0; i < self->max_fds; i++) {
123				char path[PATH_MAX];
124				int fd;
125
126				sprintf(path, "/slab_typesafe_by_rcu/file-%d-%d", self->pids_openers[i], i);
127				fd = open(path, O_CREAT | O_RDONLY | O_CLOEXEC, 0644);
128				if (fd < 0)
129					continue;
130			}
131
132			close_range(3, ~0U, 0);
133		}
134
135		exit(0);
136	}
137
138	for (int i = 0; i < self->nr_procs; i++) {
139		char path[PATH_MAX];
140
141		sprintf(path, "/proc/%d/fd/", self->pids_openers[i]);
142		self->fd_proc_pid[i] = open(path, O_DIRECTORY | O_RDONLY | O_CLOEXEC);
143		ASSERT_GE(self->fd_proc_pid[i], 0);
144	}
145
146	for (int i = 0; i < self->nr_procs; i++) {
147		self->pids_getdents[i] = fork();
148		ASSERT_GE(self->pids_getdents[i], 0);
149
150		if (self->pids_getdents[i] != 0)
151			continue;
152
153		self->pids_getdents[i] = getpid();
154		for (;;) {
155			char ents[1024];
156			ssize_t nr_read;
157
158			/*
159			 * Concurrently read /proc/<pid>/fd/ which rougly does:
160			 *
161			 * f = fget_task_next(p, &fd);
162			 * if (!f)
163			 *	break;
164			 * data.mode = f->f_mode;
165			 * fput(f);
166			 *
167			 * Which means that it'll try to get a reference to a
168			 * file in another task's file descriptor table.
169			 *
170			 * Under heavy file load it is increasingly likely that
171			 * the other task will manage to close @file and @file
172			 * is being recycled due to SLAB_TYPEAFE_BY_RCU
173			 * concurrently. This will trigger various warnings in
174			 * the file reference counting code.
175			 */
176			do {
177				nr_read = syscall(SYS_getdents64, self->fd_proc_pid[i], ents, sizeof(ents));
178			} while (nr_read >= 0);
179
180			lseek(self->fd_proc_pid[i], 0, SEEK_SET);
181		}
182
183		exit(0);
184	}
185
186	ASSERT_EQ(clock_nanosleep(CLOCK_MONOTONIC, 0, &(struct timespec){ .tv_sec = 900 /* 15 min */ }, NULL), 0);
187
188	for (int i = 0; i < self->nr_procs; i++) {
189		kill(self->pids_openers[i], SIGKILL);
190		kill(self->pids_getdents[i], SIGKILL);
191	}
192}
193
194TEST_HARNESS_MAIN