Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/selftests/filesystems/file_stressor.c
26298 views
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
21
static inline int sys_fsopen(const char *fsname, unsigned int flags)
22
{
23
return syscall(__NR_fsopen, fsname, flags);
24
}
25
26
static 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
32
static 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
42
static 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
50
FIXTURE(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
59
FIXTURE_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
87
FIXTURE_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
109
TEST_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 roughly 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
194
TEST_HARNESS_MAIN
195
196