"Fossies" - the Fresh Open Source Software Archive

Member "stress-ng-0.09.56/stress-clone.c" (15 Mar 2019, 8372 Bytes) of package /linux/privat/stress-ng-0.09.56.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "stress-clone.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 0.09.52_vs_0.09.54.

    1 /*
    2  * Copyright (C) 2013-2019 Canonical, Ltd.
    3  *
    4  * This program is free software; you can redistribute it and/or
    5  * modify it under the terms of the GNU General Public License
    6  * as published by the Free Software Foundation; either version 2
    7  * of the License, or (at your option) any later version.
    8  *
    9  * This program is distributed in the hope that it will be useful,
   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12  * GNU General Public License for more details.
   13  *
   14  * You should have received a copy of the GNU General Public License
   15  * along with this program; if not, write to the Free Software
   16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
   17  *
   18  * This code is a complete clean re-write of the stress tool by
   19  * Colin Ian King <colin.king@canonical.com> and attempts to be
   20  * backwardly compatible with the stress tool by Amos Waterland
   21  * <apw@rossby.metr.ou.edu> but has more stress tests and more
   22  * functionality.
   23  *
   24  */
   25 #include "stress-ng.h"
   26 
   27 #define CLONE_STACK_SIZE    (16*1024)
   28 
   29 typedef struct clone_args {
   30     const args_t *args;
   31 } clone_args_t;
   32 
   33 typedef struct clone {
   34     struct clone *next;
   35     pid_t   pid;
   36     char stack[CLONE_STACK_SIZE];
   37 } clone_t;
   38 
   39 typedef struct {
   40     clone_t *head;      /* Head of clone procs list */
   41     clone_t *tail;      /* Tail of clone procs list */
   42     clone_t *free;      /* List of free'd clones */
   43     uint64_t length;    /* Length of list */
   44 } clone_list_t;
   45 
   46 #if defined(HAVE_CLONE)
   47 
   48 static clone_list_t clones;
   49 
   50 /*
   51  *  A random selection of clone flags that are worth exercising
   52  */
   53 static const int flags[] = {
   54     0,
   55 #if defined(CLONE_FILES)
   56     CLONE_FILES,
   57 #endif
   58 #if defined(CLONE_FS)
   59     CLONE_FS,
   60 #endif
   61 #if defined(CLONE_IO)
   62     CLONE_IO,
   63 #endif
   64 #if defined(CLONE_NEWIPC)
   65     CLONE_NEWIPC,
   66 #endif
   67 #if defined(CLONE_NEWNET)
   68     CLONE_NEWNET,
   69 #endif
   70 #if defined(CLONE_NEWNS)
   71     CLONE_NEWNS,
   72 #endif
   73 #if defined(CLONE_NEWUSER)
   74     CLONE_NEWUSER,
   75 #endif
   76 #if defined(CLONE_NEWUTS)
   77     CLONE_NEWUTS,
   78 #endif
   79 #if defined(CLONE_SIGHAND)
   80     CLONE_SIGHAND,
   81 #endif
   82 #if defined(CLONE_SYSVSEM)
   83     CLONE_SYSVSEM,
   84 #endif
   85 #if defined(CLONE_UNTRACED)
   86     CLONE_UNTRACED,
   87 #endif
   88 };
   89 
   90 static const int unshare_flags[] = {
   91 #if defined(CLONE_FILES)
   92     CLONE_FILES,
   93 #endif
   94 #if defined(CLONE_FS)
   95     CLONE_FS,
   96 #endif
   97 #if defined(CLONE_NEWIPC)
   98     CLONE_NEWIPC,
   99 #endif
  100 #if defined(CLONE_NEWNET)
  101     CLONE_NEWNET,
  102 #endif
  103 #if defined(CLONE_NEWNS)
  104     CLONE_NEWNS,
  105 #endif
  106 #if defined(CLONE_NEWUTS)
  107     CLONE_NEWUTS,
  108 #endif
  109 #if defined(CLONE_SYSVSEM)
  110     CLONE_SYSVSEM,
  111 #endif
  112 };
  113 #endif
  114 
  115 /*
  116  *  stress_set_clone_max()
  117  *  set maximum number of clones allowed
  118  */
  119 int stress_set_clone_max(const char *opt)
  120 {
  121     uint64_t clone_max;
  122 
  123     clone_max = get_uint64(opt);
  124     check_range("clone-max", clone_max,
  125         MIN_ZOMBIES, MAX_ZOMBIES);
  126     return set_setting("clone-max", TYPE_ID_UINT64, &clone_max);
  127 }
  128 
  129 #if defined(HAVE_CLONE)
  130 
  131 /*
  132  *  stress_clone_new()
  133  *  allocate a new clone, add to end of list
  134  */
  135 static clone_t *stress_clone_new(void)
  136 {
  137     clone_t *new;
  138 
  139     if (clones.free) {
  140         /* Pop an old one off the free list */
  141         new = clones.free;
  142         clones.free = new->next;
  143         new->next = NULL;
  144     } else {
  145         new = calloc(1, sizeof(*new));
  146         if (!new)
  147             return NULL;
  148     }
  149 
  150     if (clones.head)
  151         clones.tail->next = new;
  152     else
  153         clones.head = new;
  154 
  155     clones.tail = new;
  156     clones.length++;
  157 
  158     return new;
  159 }
  160 
  161 /*
  162  *  stress_clone_head_remove
  163  *  reap a clone and remove a clone from head of list, put it onto
  164  *  the free clone list
  165  */
  166 static void stress_clone_head_remove(void)
  167 {
  168     if (clones.head) {
  169         int status;
  170         clone_t *head = clones.head;
  171 
  172         (void)waitpid(clones.head->pid, &status, __WCLONE);
  173 
  174         if (clones.tail == clones.head) {
  175             clones.tail = NULL;
  176             clones.head = NULL;
  177         } else {
  178             clones.head = head->next;
  179         }
  180 
  181         /* Shove it on the free list */
  182         head->next = clones.free;
  183         clones.free = head;
  184 
  185         clones.length--;
  186     }
  187 }
  188 
  189 /*
  190  *  stress_clone_free()
  191  *  free the clones off the clone free lists
  192  */
  193 static void stress_clone_free(void)
  194 {
  195     while (clones.head) {
  196         clone_t *next = clones.head->next;
  197 
  198         free(clones.head);
  199         clones.head = next;
  200     }
  201     while (clones.free) {
  202         clone_t *next = clones.free->next;
  203 
  204         free(clones.free);
  205         clones.free = next;
  206     }
  207 }
  208 
  209 /*
  210  *  clone_func()
  211  *  clone thread just returns immediately
  212  */
  213 static int clone_func(void *arg)
  214 {
  215     size_t i;
  216     clone_args_t *clone_arg = arg;
  217 
  218     (void)arg;
  219 
  220     set_oom_adjustment(clone_arg->args->name, true);
  221 #if defined(HAVE_SETNS)
  222     {
  223         int fd;
  224 
  225         fd = open("/proc/self/ns/uts", O_RDONLY);
  226         if (fd >= 0) {
  227             /*
  228              *  Capabilities have been dropped
  229              *  so this will always fail, but
  230              *  lets exercise it anyhow.
  231              */
  232             (void)setns(fd, 0);
  233             (void)close(fd);
  234         }
  235     }
  236 #endif
  237 
  238 #if defined(HAVE_MODIFY_LDT)
  239     {
  240         struct user_desc ud;
  241         int ret;
  242 
  243         (void)memset(&ud, 0, sizeof(ud));
  244         ret = syscall(__NR_modify_ldt, 0, &ud, sizeof(ud));
  245         if (ret == 0) {
  246             ret = syscall(__NR_modify_ldt, 1, &ud, sizeof(ud));
  247             (void)ret;
  248         }
  249     }
  250 #endif
  251     for (i = 0; i < SIZEOF_ARRAY(unshare_flags); i++) {
  252         (void)shim_unshare(unshare_flags[i]);
  253     }
  254 
  255     return 0;
  256 }
  257 
  258 /*
  259  *  stress_clone()
  260  *  stress by cloning and exiting
  261  */
  262 static int stress_clone(const args_t *args)
  263 {
  264     uint64_t max_clones = 0;
  265     uint64_t clone_max = DEFAULT_ZOMBIES;
  266     pid_t pid;
  267     const ssize_t stack_offset =
  268         stress_get_stack_direction() *
  269         (CLONE_STACK_SIZE - 64);
  270 
  271     if (!get_setting("clone-max", &clone_max)) {
  272         if (g_opt_flags & OPT_FLAGS_MAXIMIZE)
  273             clone_max = MAX_ZOMBIES;
  274         if (g_opt_flags & OPT_FLAGS_MINIMIZE)
  275             clone_max = MIN_ZOMBIES;
  276     }
  277 
  278 again:
  279     if (!g_keep_stressing_flag)
  280         return EXIT_SUCCESS;
  281     pid = fork();
  282     if (pid < 0) {
  283         if ((errno == EAGAIN) || (errno == ENOMEM))
  284             goto again;
  285         pr_err("%s: fork failed: errno=%d: (%s)\n",
  286             args->name, errno, strerror(errno));
  287     } else if (pid > 0) {
  288         int status, ret;
  289 
  290         (void)setpgid(pid, g_pgrp);
  291         /* Parent, wait for child */
  292         ret = waitpid(pid, &status, 0);
  293         if (ret < 0) {
  294             if (errno != EINTR)
  295                 pr_dbg("%s: waitpid(): errno=%d (%s)\n",
  296                     args->name, errno, strerror(errno));
  297             (void)kill(pid, SIGALRM);
  298             (void)waitpid(pid, &status, 0);
  299             /* And kill it for sure */
  300             (void)kill(pid, SIGKILL);
  301         } else if (WIFSIGNALED(status)) {
  302             pr_dbg("%s: child died: %s (instance %d)\n",
  303                 args->name, stress_strsignal(WTERMSIG(status)),
  304                 args->instance);
  305             /* If we got killed by OOM killer, re-start */
  306             if ((WTERMSIG(status) == SIGKILL) ||
  307                 (WTERMSIG(status) == SIGTERM)) {
  308                 if (g_opt_flags & OPT_FLAGS_OOMABLE) {
  309                     log_system_mem_info();
  310                     pr_dbg("%s: assuming killed by OOM "
  311                         "killer, bailing out "
  312                         "(instance %d)\n",
  313                         args->name, args->instance);
  314                     _exit(0);
  315                 } else {
  316                     log_system_mem_info();
  317                     pr_dbg("%s: assuming killed by OOM "
  318                         "killer, restarting again "
  319                         "(instance %d)\n",
  320                         args->name, args->instance);
  321                     goto again;
  322                 }
  323             }
  324         }
  325     } else if (pid == 0) {
  326         /* Child */
  327         int ret;
  328 
  329         (void)setpgid(0, g_pgrp);
  330         stress_parent_died_alarm();
  331 
  332         /* Make sure this is killable by OOM killer */
  333         set_oom_adjustment(args->name, true);
  334 
  335         /* Explicitly drop capabilites, makes it more OOM-able */
  336         ret = stress_drop_capabilities(args->name);
  337         (void)ret;
  338 
  339         do {
  340             if (clones.length < clone_max) {
  341                 clone_t *clone_info;
  342                 clone_args_t clone_arg = { args };
  343                 char *stack_top;
  344                 int flag = flags[mwc32() % SIZEOF_ARRAY(flags)];
  345 
  346                 clone_info = stress_clone_new();
  347                 if (!clone_info)
  348                     break;
  349                 stack_top = clone_info->stack + stack_offset;
  350                 clone_info->pid = clone(clone_func,
  351                     align_stack(stack_top), flag, &clone_arg);
  352                 if (clone_info->pid == -1) {
  353                     /*
  354                      * Reached max forks or error
  355                      * (e.g. EPERM)? .. then reap
  356                      */
  357                     stress_clone_head_remove();
  358                     continue;
  359                 }
  360                 if (max_clones < clones.length)
  361                     max_clones = clones.length;
  362                 inc_counter(args);
  363             } else {
  364                 stress_clone_head_remove();
  365             }
  366         } while (keep_stressing());
  367 
  368         pr_inf("%s: created a maximum of %" PRIu64 " clones\n",
  369             args->name, max_clones);
  370         /* And reap */
  371         while (clones.head) {
  372             stress_clone_head_remove();
  373         }
  374         /* And free */
  375         stress_clone_free();
  376 
  377         _exit(0);
  378     }
  379 
  380     return EXIT_SUCCESS;
  381 }
  382 
  383 stressor_info_t stress_clone_info = {
  384     .stressor = stress_clone,
  385     .class = CLASS_SCHEDULER | CLASS_OS
  386 };
  387 #else
  388 stressor_info_t stress_clone_info = {
  389     .stressor = stress_not_implemented,
  390     .class = CLASS_SCHEDULER | CLASS_OS
  391 };
  392 #endif