Path: blob/master/tools/testing/selftests/filesystems/file_stressor.c
26298 views
// SPDX-License-Identifier: GPL-2.01#define _GNU_SOURCE2#define __SANE_USERSPACE_TYPES__34#include <fcntl.h>5#include <limits.h>6#include <pthread.h>7#include <sched.h>8#include <stdio.h>9#include <string.h>10#include <sys/stat.h>11#include <sys/mount.h>12#include <unistd.h>1314#include "../kselftest_harness.h"1516#include <linux/types.h>17#include <linux/mount.h>18#include <sys/syscall.h>1920static inline int sys_fsopen(const char *fsname, unsigned int flags)21{22return syscall(__NR_fsopen, fsname, flags);23}2425static inline int sys_fsconfig(int fd, unsigned int cmd, const char *key,26const char *value, int aux)27{28return syscall(__NR_fsconfig, fd, cmd, key, value, aux);29}3031static inline int sys_fsmount(int fd, unsigned int flags,32unsigned int attr_flags)33{34return syscall(__NR_fsmount, fd, flags, attr_flags);35}3637#ifndef MOVE_MOUNT_F_EMPTY_PATH38#define MOVE_MOUNT_F_EMPTY_PATH 0x00000004 /* Empty from path permitted */39#endif4041static inline int sys_move_mount(int from_dfd, const char *from_pathname,42int to_dfd, const char *to_pathname,43unsigned int flags)44{45return syscall(__NR_move_mount, from_dfd, from_pathname, to_dfd,46to_pathname, flags);47}4849FIXTURE(file_stressor) {50int fd_tmpfs;51int nr_procs;52int max_fds;53pid_t *pids_openers;54pid_t *pids_getdents;55int *fd_proc_pid;56};5758FIXTURE_SETUP(file_stressor)59{60int fd_context;6162ASSERT_EQ(unshare(CLONE_NEWNS), 0);63ASSERT_EQ(mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL), 0);64ASSERT_EQ(mkdir("/slab_typesafe_by_rcu", 0755), 0);6566fd_context = sys_fsopen("tmpfs", 0);67ASSERT_GE(fd_context, 0);6869ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_CMD_CREATE, NULL, NULL, 0), 0);70self->fd_tmpfs = sys_fsmount(fd_context, 0, 0);71ASSERT_GE(self->fd_tmpfs, 0);72ASSERT_EQ(close(fd_context), 0);7374ASSERT_EQ(sys_move_mount(self->fd_tmpfs, "", -EBADF, "/slab_typesafe_by_rcu", MOVE_MOUNT_F_EMPTY_PATH), 0);7576self->nr_procs = sysconf(_SC_NPROCESSORS_ONLN);77self->pids_openers = malloc(sizeof(pid_t) * self->nr_procs);78ASSERT_NE(self->pids_openers, NULL);79self->pids_getdents = malloc(sizeof(pid_t) * self->nr_procs);80ASSERT_NE(self->pids_getdents, NULL);81self->fd_proc_pid = malloc(sizeof(int) * self->nr_procs);82ASSERT_NE(self->fd_proc_pid, NULL);83self->max_fds = 500;84}8586FIXTURE_TEARDOWN(file_stressor)87{88for (int i = 0; i < self->nr_procs; i++) {89int wstatus;90pid_t pid;9192pid = waitpid(self->pids_openers[i], &wstatus, 0);93ASSERT_EQ(pid, self->pids_openers[i]);94ASSERT_TRUE(!WIFEXITED(wstatus) || !WIFSIGNALED(wstatus));9596pid = waitpid(self->pids_getdents[i], &wstatus, 0);97ASSERT_EQ(pid, self->pids_getdents[i]);98ASSERT_TRUE(!WIFEXITED(wstatus) || !WIFSIGNALED(wstatus));99}100free(self->pids_openers);101free(self->pids_getdents);102ASSERT_EQ(close(self->fd_tmpfs), 0);103104umount2("/slab_typesafe_by_rcu", 0);105ASSERT_EQ(rmdir("/slab_typesafe_by_rcu"), 0);106}107108TEST_F_TIMEOUT(file_stressor, slab_typesafe_by_rcu, 900 * 2)109{110for (int i = 0; i < self->nr_procs; i++) {111pid_t pid_self;112113self->pids_openers[i] = fork();114ASSERT_GE(self->pids_openers[i], 0);115116if (self->pids_openers[i] != 0)117continue;118119self->pids_openers[i] = getpid();120for (;;) {121for (int i = 0; i < self->max_fds; i++) {122char path[PATH_MAX];123int fd;124125sprintf(path, "/slab_typesafe_by_rcu/file-%d-%d", self->pids_openers[i], i);126fd = open(path, O_CREAT | O_RDONLY | O_CLOEXEC, 0644);127if (fd < 0)128continue;129}130131close_range(3, ~0U, 0);132}133134exit(0);135}136137for (int i = 0; i < self->nr_procs; i++) {138char path[PATH_MAX];139140sprintf(path, "/proc/%d/fd/", self->pids_openers[i]);141self->fd_proc_pid[i] = open(path, O_DIRECTORY | O_RDONLY | O_CLOEXEC);142ASSERT_GE(self->fd_proc_pid[i], 0);143}144145for (int i = 0; i < self->nr_procs; i++) {146self->pids_getdents[i] = fork();147ASSERT_GE(self->pids_getdents[i], 0);148149if (self->pids_getdents[i] != 0)150continue;151152self->pids_getdents[i] = getpid();153for (;;) {154char ents[1024];155ssize_t nr_read;156157/*158* Concurrently read /proc/<pid>/fd/ which roughly does:159*160* f = fget_task_next(p, &fd);161* if (!f)162* break;163* data.mode = f->f_mode;164* fput(f);165*166* Which means that it'll try to get a reference to a167* file in another task's file descriptor table.168*169* Under heavy file load it is increasingly likely that170* the other task will manage to close @file and @file171* is being recycled due to SLAB_TYPEAFE_BY_RCU172* concurrently. This will trigger various warnings in173* the file reference counting code.174*/175do {176nr_read = syscall(SYS_getdents64, self->fd_proc_pid[i], ents, sizeof(ents));177} while (nr_read >= 0);178179lseek(self->fd_proc_pid[i], 0, SEEK_SET);180}181182exit(0);183}184185ASSERT_EQ(clock_nanosleep(CLOCK_MONOTONIC, 0, &(struct timespec){ .tv_sec = 900 /* 15 min */ }, NULL), 0);186187for (int i = 0; i < self->nr_procs; i++) {188kill(self->pids_openers[i], SIGKILL);189kill(self->pids_getdents[i], SIGKILL);190}191}192193TEST_HARNESS_MAIN194195196