1 Star 0 Fork 2

gaoxuelong / stress-ng

forked from HoperunHarmony / stress-ng 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
stress-clone.c 10.94 KB
一键复制 编辑 原始数据 按行查看 历史
/*
* Copyright (C) 2013-2021 Canonical, Ltd.
* Copyright (C) 2022 Colin Ian King.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "stress-ng.h"
#if defined(HAVE_MODIFY_LDT)
#include <asm/ldt.h>
#endif
#define MIN_CLONES (1)
#define MAX_CLONES (1000000)
#define DEFAULT_CLONES (8192)
#define CLONE_STACK_SIZE (8*1024)
#if defined(HAVE_CLONE)
typedef struct stress_clone_args {
const stress_args_t *args;
} stress_clone_args_t;
typedef struct clone {
struct clone *next;
pid_t pid;
uint64_t stack[CLONE_STACK_SIZE / sizeof(uint64_t)];
} stress_clone_t;
typedef struct {
stress_clone_t *head; /* Head of clone procs list */
stress_clone_t *tail; /* Tail of clone procs list */
stress_clone_t *free; /* List of free'd clones */
uint32_t length; /* Length of list */
} stress_clone_list_t;
#endif
static const stress_help_t help[] = {
{ NULL, "clone N", "start N workers that rapidly create and reap clones" },
{ NULL, "clone-ops N", "stop after N bogo clone operations" },
{ NULL, "clone-max N", "set upper limit of N clones per worker" },
{ NULL, NULL, NULL }
};
#if defined(HAVE_CLONE)
static stress_clone_list_t clones;
/*
* A random selection of clone flags that are worth exercising
*/
static const uint64_t flags[] = {
0,
/*
* Avoid CLONE_VM for now as child may memory clobber parent
#if defined(CLONE_SIGHAND) && \
defined(CLONE_VM)
CLONE_SIGHAND | CLONE_VM,
#endif
*/
#if defined(CLONE_FS)
CLONE_FS,
#endif
#if defined(CLONE_FILES)
CLONE_FILES,
#endif
#if defined(CLONE_SIGHAND)
CLONE_SIGHAND,
#endif
#if defined(CLONE_PIDFD)
CLONE_PIDFD,
#endif
/*
#if defined(CLONE_PTRACE)
CLONE_PTRACE,
#endif
*/
/*
#if defined(CLONE_VFORK)
CLONE_VFORK,
#endif
*/
#if defined(CLONE_PARENT)
CLONE_PARENT,
#endif
#if defined(CLONE_THREAD)
CLONE_THREAD,
#endif
#if defined(CLONE_NEWNS)
CLONE_NEWNS,
#endif
#if defined(CLONE_SYSVSEM)
CLONE_SYSVSEM,
#endif
/*
#if defined(CLONE_SETTLS)
CLONE_SETTLS,
#endif
*/
#if defined(CLONE_PARENT_SETTID)
CLONE_PARENT_SETTID,
#endif
#if defined(CLONE_CHILD_CLEARTID)
CLONE_CHILD_CLEARTID,
#endif
#if defined(CLONE_DETACHED)
CLONE_DETACHED,
#endif
#if defined(CLONE_UNTRACED)
CLONE_UNTRACED,
#endif
#if defined(CLONE_CHILD_SETTID)
CLONE_CHILD_SETTID,
#endif
#if defined(CLONE_NEWCGROUP)
CLONE_NEWCGROUP,
#endif
#if defined(CLONE_NEWUTS)
CLONE_NEWUTS,
#endif
#if defined(CLONE_NEWIPC)
CLONE_NEWIPC,
#endif
#if defined(CLONE_NEWUSER)
CLONE_NEWUSER,
#endif
#if defined(CLONE_NEWPID)
CLONE_NEWPID,
#endif
#if defined(CLONE_NEWNET)
CLONE_NEWNET,
#endif
#if defined(CLONE_IO)
CLONE_IO,
#endif
#if defined(CLONE_CLEAR_SIGHAND)
CLONE_CLEAR_SIGHAND,
#endif
#if defined(CLONE_INTO_CGROUP)
CLONE_INTO_CGROUP,
#endif
#if defined(CLONE_NEWTIME)
CLONE_NEWTIME,
#endif
};
static const uint64_t unshare_flags[] = {
#if defined(CLONE_FILES)
CLONE_FILES,
#endif
#if defined(CLONE_FS)
CLONE_FS,
#endif
#if defined(CLONE_NEWIPC)
CLONE_NEWIPC,
#endif
#if defined(CLONE_NEWNET)
CLONE_NEWNET,
#endif
#if defined(CLONE_NEWNS)
CLONE_NEWNS,
#endif
#if defined(CLONE_NEWUTS)
CLONE_NEWUTS,
#endif
#if defined(CLONE_SYSVSEM)
CLONE_SYSVSEM,
#endif
#if defined(CLONE_NEWCGROUP)
CLONE_NEWCGROUP,
#endif
};
#endif
/*
* stress_set_clone_max()
* set maximum number of clones allowed
*/
static int stress_set_clone_max(const char *opt)
{
uint32_t clone_max;
clone_max = stress_get_uint32(opt);
stress_check_range("clone-max", clone_max,
MIN_CLONES, MAX_CLONES);
return stress_set_setting("clone-max", TYPE_ID_UINT32, &clone_max);
}
static const stress_opt_set_func_t opt_set_funcs[] = {
{ OPT_clone_max, stress_set_clone_max },
{ 0, NULL }
};
#if defined(HAVE_CLONE)
static inline uint64_t uint64_ptr(const void *ptr)
{
return (uint64_t)(uintptr_t)ptr;
}
/*
* stress_clone_new()
* allocate a new clone, add to end of list
*/
static stress_clone_t *stress_clone_new(void)
{
stress_clone_t *new;
if (clones.free) {
/* Pop an old one off the free list */
new = clones.free;
clones.free = new->next;
new->next = NULL;
} else {
new = mmap(NULL, sizeof(*new), PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (new == MAP_FAILED)
return NULL;
}
if (clones.head)
clones.tail->next = new;
else
clones.head = new;
clones.tail = new;
clones.length++;
return new;
}
/*
* stress_clone_head_remove
* reap a clone and remove a clone from head of list, put it onto
* the free clone list
*/
static void stress_clone_head_remove(void)
{
if (clones.head) {
int status;
stress_clone_t *head = clones.head;
(void)waitpid(clones.head->pid, &status, (int)__WCLONE);
if (clones.tail == clones.head) {
clones.tail = NULL;
clones.head = NULL;
} else {
clones.head = head->next;
}
/* Shove it on the free list */
head->next = clones.free;
clones.free = head;
clones.length--;
}
}
/*
* stress_clone_free()
* free the clones off the clone free lists
*/
static void stress_clone_free(void)
{
while (clones.head) {
stress_clone_t *next = clones.head->next;
(void)munmap((void *)clones.head, sizeof(*(clones.head)));
clones.head = next;
}
while (clones.free) {
stress_clone_t *next = clones.free->next;
(void)munmap((void *)clones.free, sizeof(*(clones.free)));
clones.free = next;
}
}
/*
* clone_func()
* clone thread just returns immediately
*/
static int clone_func(void *arg)
{
size_t i;
stress_clone_args_t *clone_arg = arg;
(void)arg;
stress_set_oom_adjustment(clone_arg->args->name, true);
#if defined(HAVE_SETNS)
{
int fd;
fd = open("/proc/self/ns/uts", O_RDONLY);
if (fd >= 0) {
/* Exercise invalid setns nstype, EINVAL */
(void)setns(fd, ~0);
/* Exercise invalid setns fd, EBADF */
(void)setns(~0, 0);
/*
* Capabilities have been dropped
* so this will always fail, but
* lets exercise it anyhow.
*/
(void)setns(fd, 0);
(void)close(fd);
}
}
#else
UNEXPECTED
#endif
#if defined(HAVE_MODIFY_LDT) && \
defined(__NR_modify_ldt)
{
struct user_desc ud;
(void)memset(&ud, 0, sizeof(ud));
if (shim_modify_ldt(0, &ud, sizeof(ud)) == 0) {
(void)shim_modify_ldt(1, &ud, sizeof(ud));
/* Exercise invalid size */
(void)shim_modify_ldt(1, &ud, 1);
/* Exercise invalid entries */
ud.entry_number = ~0;
(void)shim_modify_ldt(1, &ud, sizeof(ud));
}
(void)memset(&ud, 0, sizeof(ud));
if (shim_modify_ldt(0, &ud, sizeof(ud)) == 0) {
/* Old mode style */
(void)shim_modify_ldt(0x11, &ud, sizeof(ud));
}
(void)memset(&ud, 0, sizeof(ud));
(void)shim_modify_ldt(2, &ud, sizeof(ud));
/* Exercise invalid command */
(void)shim_modify_ldt(0xff, &ud, sizeof(ud));
/* Exercise invalid ldt size */
(void)memset(&ud, 0, sizeof(ud));
(void)shim_modify_ldt(0, &ud, 0);
}
#endif
for (i = 0; i < SIZEOF_ARRAY(unshare_flags); i++) {
(void)shim_unshare((int)unshare_flags[i]);
}
return 0;
}
static int stress_clone_child(const stress_args_t *args, void *context)
{
/* Child */
uint32_t max_clones = 0;
uint32_t clone_max = DEFAULT_CLONES;
bool use_clone3 = true;
const size_t mmap_size = args->page_size * 32768;
void *ptr;
#if defined(MAP_POPULATE)
const int mflags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_POPULATE;
#else
const int mflags = MAP_ANONYMOUS | MAP_PRIVATE;
#endif
(void)context;
if (!stress_get_setting("clone-max", &clone_max)) {
if (g_opt_flags & OPT_FLAGS_MAXIMIZE)
clone_max = MAX_CLONES;
if (g_opt_flags & OPT_FLAGS_MINIMIZE)
clone_max = MIN_CLONES;
}
/*
* Make child larger than parent to make it more of
* a candidate for a OOMable process
*/
ptr = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, mflags, -1, 0);
if (ptr != MAP_FAILED)
(void)stress_mincore_touch_pages(ptr, mmap_size);
do {
if (clones.length < clone_max) {
stress_clone_t *clone_info;
stress_clone_args_t clone_arg = { args };
const uint32_t rnd = stress_mwc32();
const uint64_t flag = flags[rnd % SIZEOF_ARRAY(flags)]; /* cppcheck-suppress moduloofone */
const bool try_clone3 = rnd >> 31;
pid_t child_tid = -1, parent_tid = -1;
clone_info = stress_clone_new();
if (!clone_info)
break;
if (use_clone3 && try_clone3) {
struct shim_clone_args cl_args;
int pidfd = -1;
memset(&cl_args, 0, sizeof(cl_args));
cl_args.flags = flag;
cl_args.pidfd = uint64_ptr(&pidfd);
cl_args.child_tid = uint64_ptr(&child_tid);
cl_args.parent_tid = uint64_ptr(&parent_tid);
cl_args.exit_signal = SIGCHLD;
cl_args.stack = uint64_ptr(NULL);
cl_args.stack_size = 0;
cl_args.tls = uint64_ptr(NULL);
clone_info->pid = sys_clone3(&cl_args, sizeof(cl_args));
if (clone_info->pid < 0) {
/* Not available, don't use it again */
if (errno == ENOSYS)
use_clone3 = false;
} else if (clone_info->pid == 0) {
/* child */
_exit(clone_func(&clone_arg));
}
} else {
char *stack_top = (char *)stress_get_stack_top((char *)clone_info->stack, CLONE_STACK_SIZE);
#if defined(__FreeBSD_kernel__) || \
defined(__NetBSD__)
clone_info->pid = clone(clone_func,
stress_align_stack(stack_top), (int)flag, &clone_arg);
#else
clone_info->pid = clone(clone_func,
stress_align_stack(stack_top), (int)flag, &clone_arg, &parent_tid,
NULL, &child_tid);
#endif
}
if (clone_info->pid == -1) {
/*
* Reached max forks or error
* (e.g. EPERM)? .. then reap
*/
stress_clone_head_remove();
continue;
}
if (max_clones < clones.length)
max_clones = clones.length;
inc_counter(args);
} else {
stress_clone_head_remove();
}
} while (keep_stressing(args));
if (ptr != MAP_FAILED)
(void)munmap(ptr, mmap_size);
/* And reap */
while (clones.head) {
stress_clone_head_remove();
}
/* And free */
stress_clone_free();
return EXIT_SUCCESS;
}
/*
* stress_clone()
* stress by cloning and exiting
*/
static int stress_clone(const stress_args_t *args)
{
int rc;
stress_set_oom_adjustment(args->name, false);
stress_set_proc_state(args->name, STRESS_STATE_RUN);
rc = stress_oomable_child(args, NULL, stress_clone_child, STRESS_OOMABLE_DROP_CAP);
stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
return rc;
}
stressor_info_t stress_clone_info = {
.stressor = stress_clone,
.class = CLASS_SCHEDULER | CLASS_OS,
.opt_set_funcs = opt_set_funcs,
.help = help
};
#else
stressor_info_t stress_clone_info = {
.stressor = stress_not_implemented,
.class = CLASS_SCHEDULER | CLASS_OS,
.opt_set_funcs = opt_set_funcs,
.help = help
};
#endif
1
https://gitee.com/gaoxuelong/stress-ng.git
git@gitee.com:gaoxuelong/stress-ng.git
gaoxuelong
stress-ng
stress-ng
master

搜索帮助

53164aa7 5694891 3bd8fe86 5694891