"Fossies" - the Fresh Open Source Software Archive

Member "stress-ng-0.09.59.1/stress-clone.c" (8 Jun 2019, 9429 Bytes) of package /linux/privat/stress-ng-0.09.59.1.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.58_vs_0.09.59.

    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 static const help_t help[] = {
   47     { NULL, "clone N",  "start N workers that rapidly create and reap clones" },
   48     { NULL, "clone-ops N",  "stop after N bogo clone operations" },
   49     { NULL, "clone-max N",  "set upper limit of N clones per worker" },
   50     { NULL, NULL,       NULL }
   51 };
   52 
   53 #if defined(HAVE_CLONE)
   54 
   55 static clone_list_t clones;
   56 
   57 /*
   58  *  A random selection of clone flags that are worth exercising
   59  */
   60 static const int flags[] = {
   61     0,
   62 #if defined(CLONE_FILES)
   63     CLONE_FILES,
   64 #endif
   65 #if defined(CLONE_FS)
   66     CLONE_FS,
   67 #endif
   68 #if defined(CLONE_IO)
   69     CLONE_IO,
   70 #endif
   71 #if defined(CLONE_NEWIPC)
   72     CLONE_NEWIPC,
   73 #endif
   74 #if defined(CLONE_NEWNET)
   75     CLONE_NEWNET,
   76 #endif
   77 #if defined(CLONE_NEWNS)
   78     CLONE_NEWNS,
   79 #endif
   80 #if defined(CLONE_NEWUSER)
   81     CLONE_NEWUSER,
   82 #endif
   83 #if defined(CLONE_NEWUTS)
   84     CLONE_NEWUTS,
   85 #endif
   86 #if defined(CLONE_SIGHAND)
   87     CLONE_SIGHAND,
   88 #endif
   89 #if defined(CLONE_SYSVSEM)
   90     CLONE_SYSVSEM,
   91 #endif
   92 #if defined(CLONE_UNTRACED)
   93     CLONE_UNTRACED,
   94 #endif
   95 };
   96 
   97 static const int unshare_flags[] = {
   98 #if defined(CLONE_FILES)
   99     CLONE_FILES,
  100 #endif
  101 #if defined(CLONE_FS)
  102     CLONE_FS,
  103 #endif
  104 #if defined(CLONE_NEWIPC)
  105     CLONE_NEWIPC,
  106 #endif
  107 #if defined(CLONE_NEWNET)
  108     CLONE_NEWNET,
  109 #endif
  110 #if defined(CLONE_NEWNS)
  111     CLONE_NEWNS,
  112 #endif
  113 #if defined(CLONE_NEWUTS)
  114     CLONE_NEWUTS,
  115 #endif
  116 #if defined(CLONE_SYSVSEM)
  117     CLONE_SYSVSEM,
  118 #endif
  119 };
  120 #endif
  121 
  122 /*
  123  *  stress_set_clone_max()
  124  *  set maximum number of clones allowed
  125  */
  126 static int stress_set_clone_max(const char *opt)
  127 {
  128     uint64_t clone_max;
  129 
  130     clone_max = get_uint64(opt);
  131     check_range("clone-max", clone_max,
  132         MIN_ZOMBIES, MAX_ZOMBIES);
  133     return set_setting("clone-max", TYPE_ID_UINT64, &clone_max);
  134 }
  135 
  136 static const opt_set_func_t opt_set_funcs[] = {
  137     { OPT_clone_max,    stress_set_clone_max },
  138     { 0,            NULL }
  139 };
  140 
  141 #if defined(HAVE_CLONE)
  142 
  143 /*
  144  *  stress_clone_new()
  145  *  allocate a new clone, add to end of list
  146  */
  147 static clone_t *stress_clone_new(void)
  148 {
  149     clone_t *new;
  150 
  151     if (clones.free) {
  152         /* Pop an old one off the free list */
  153         new = clones.free;
  154         clones.free = new->next;
  155         new->next = NULL;
  156     } else {
  157         new = calloc(1, sizeof(*new));
  158         if (!new)
  159             return NULL;
  160     }
  161 
  162     if (clones.head)
  163         clones.tail->next = new;
  164     else
  165         clones.head = new;
  166 
  167     clones.tail = new;
  168     clones.length++;
  169 
  170     return new;
  171 }
  172 
  173 /*
  174  *  stress_clone_head_remove
  175  *  reap a clone and remove a clone from head of list, put it onto
  176  *  the free clone list
  177  */
  178 static void stress_clone_head_remove(void)
  179 {
  180     if (clones.head) {
  181         int status;
  182         clone_t *head = clones.head;
  183 
  184         (void)shim_waitpid(clones.head->pid, &status, __WCLONE);
  185 
  186         if (clones.tail == clones.head) {
  187             clones.tail = NULL;
  188             clones.head = NULL;
  189         } else {
  190             clones.head = head->next;
  191         }
  192 
  193         /* Shove it on the free list */
  194         head->next = clones.free;
  195         clones.free = head;
  196 
  197         clones.length--;
  198     }
  199 }
  200 
  201 /*
  202  *  stress_clone_free()
  203  *  free the clones off the clone free lists
  204  */
  205 static void stress_clone_free(void)
  206 {
  207     while (clones.head) {
  208         clone_t *next = clones.head->next;
  209 
  210         free(clones.head);
  211         clones.head = next;
  212     }
  213     while (clones.free) {
  214         clone_t *next = clones.free->next;
  215 
  216         free(clones.free);
  217         clones.free = next;
  218     }
  219 }
  220 
  221 /*
  222  *  clone_func()
  223  *  clone thread just returns immediately
  224  */
  225 static int clone_func(void *arg)
  226 {
  227     size_t i;
  228     clone_args_t *clone_arg = arg;
  229 
  230     (void)arg;
  231 
  232     set_oom_adjustment(clone_arg->args->name, true);
  233 #if defined(HAVE_SETNS)
  234     {
  235         int fd;
  236 
  237         fd = open("/proc/self/ns/uts", O_RDONLY);
  238         if (fd >= 0) {
  239             /*
  240              *  Capabilities have been dropped
  241              *  so this will always fail, but
  242              *  lets exercise it anyhow.
  243              */
  244             (void)setns(fd, 0);
  245             (void)close(fd);
  246         }
  247     }
  248 #endif
  249 
  250 #if defined(HAVE_MODIFY_LDT)
  251     {
  252         struct user_desc ud;
  253         int ret;
  254 
  255         (void)memset(&ud, 0, sizeof(ud));
  256         ret = syscall(__NR_modify_ldt, 0, &ud, sizeof(ud));
  257         if (ret == 0) {
  258             ret = syscall(__NR_modify_ldt, 1, &ud, sizeof(ud));
  259             (void)ret;
  260         }
  261     }
  262 #endif
  263     for (i = 0; i < SIZEOF_ARRAY(unshare_flags); i++) {
  264         (void)shim_unshare(unshare_flags[i]);
  265     }
  266 
  267     return 0;
  268 }
  269 
  270 /*
  271  *  stress_clone()
  272  *  stress by cloning and exiting
  273  */
  274 static int stress_clone(const args_t *args)
  275 {
  276     uint64_t max_clones = 0;
  277     uint64_t clone_max = DEFAULT_ZOMBIES;
  278     pid_t pid;
  279     const ssize_t stack_offset =
  280         stress_get_stack_direction() *
  281         (CLONE_STACK_SIZE - 64);
  282 
  283     if (!get_setting("clone-max", &clone_max)) {
  284         if (g_opt_flags & OPT_FLAGS_MAXIMIZE)
  285             clone_max = MAX_ZOMBIES;
  286         if (g_opt_flags & OPT_FLAGS_MINIMIZE)
  287             clone_max = MIN_ZOMBIES;
  288     }
  289 
  290     set_oom_adjustment(args->name, false);
  291 again:
  292     if (!g_keep_stressing_flag)
  293         return EXIT_SUCCESS;
  294     pid = fork();
  295     if (pid < 0) {
  296         if ((errno == EAGAIN) || (errno == ENOMEM))
  297             goto again;
  298         pr_err("%s: fork failed: errno=%d: (%s)\n",
  299             args->name, errno, strerror(errno));
  300     } else if (pid > 0) {
  301         int status, ret;
  302 
  303         (void)setpgid(pid, g_pgrp);
  304         /* Parent, wait for child */
  305         ret = shim_waitpid(pid, &status, 0);
  306         if (ret < 0) {
  307             if (errno != EINTR)
  308                 pr_dbg("%s: waitpid(): errno=%d (%s)\n",
  309                     args->name, errno, strerror(errno));
  310             (void)kill(pid, SIGALRM);
  311             (void)shim_waitpid(pid, &status, 0);
  312             /* And kill it for sure */
  313             (void)kill(pid, SIGKILL);
  314         } else if (WIFSIGNALED(status)) {
  315             pr_dbg("%s: child died: %s (instance %d)\n",
  316                 args->name, stress_strsignal(WTERMSIG(status)),
  317                 args->instance);
  318             /* If we got killed by OOM killer, re-start */
  319             if ((WTERMSIG(status) == SIGKILL) ||
  320                 (WTERMSIG(status) == SIGTERM)) {
  321                 if (g_opt_flags & OPT_FLAGS_OOMABLE) {
  322                     log_system_mem_info();
  323                     pr_dbg("%s: assuming killed by OOM "
  324                         "killer, bailing out "
  325                         "(instance %d)\n",
  326                         args->name, args->instance);
  327                     _exit(0);
  328                 } else {
  329                     log_system_mem_info();
  330                     pr_dbg("%s: assuming killed by OOM "
  331                         "killer, restarting again "
  332                         "(instance %d)\n",
  333                         args->name, args->instance);
  334                     goto again;
  335                 }
  336             }
  337         }
  338     } else if (pid == 0) {
  339         /* Child */
  340         int ret;
  341         const size_t mmap_size = args->page_size * 8192;
  342         void *ptr;
  343 #if defined(MAP_POPULATE)
  344         const int mflags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_POPULATE;
  345 #else
  346         const int mflags = MAP_ANONYMOUS | MAP_PRIVATE;
  347 #endif
  348 
  349         (void)setpgid(0, g_pgrp);
  350         stress_parent_died_alarm();
  351 
  352         /* Make sure this is killable by OOM killer */
  353         set_oom_adjustment(args->name, true);
  354 
  355         /* Explicitly drop capabilites, makes it more OOM-able */
  356         ret = stress_drop_capabilities(args->name);
  357         (void)ret;
  358 
  359         /*
  360          * Make child larger than parent to make it more of
  361          * a candidate for a OOMable process
  362          */
  363         ptr = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, mflags, -1, 0);
  364         if (ptr != MAP_FAILED)
  365             (void)mincore_touch_pages(ptr, mmap_size);
  366 
  367         do {
  368             if (clones.length < clone_max) {
  369                 clone_t *clone_info;
  370                 clone_args_t clone_arg = { args };
  371                 char *stack_top;
  372                 int flag = flags[mwc32() % SIZEOF_ARRAY(flags)];
  373 
  374                 clone_info = stress_clone_new();
  375                 if (!clone_info)
  376                     break;
  377                 stack_top = clone_info->stack + stack_offset;
  378                 clone_info->pid = clone(clone_func,
  379                     align_stack(stack_top), flag, &clone_arg);
  380                 if (clone_info->pid == -1) {
  381                     /*
  382                      * Reached max forks or error
  383                      * (e.g. EPERM)? .. then reap
  384                      */
  385                     stress_clone_head_remove();
  386                     continue;
  387                 }
  388                 if (max_clones < clones.length)
  389                     max_clones = clones.length;
  390                 inc_counter(args);
  391             } else {
  392                 stress_clone_head_remove();
  393             }
  394         } while (keep_stressing());
  395 
  396         pr_inf("%s: created a maximum of %" PRIu64 " clones\n",
  397             args->name, max_clones);
  398 
  399         if (ptr != MAP_FAILED)
  400             (void)munmap(ptr, mmap_size);
  401         /* And reap */
  402         while (clones.head) {
  403             stress_clone_head_remove();
  404         }
  405         /* And free */
  406         stress_clone_free();
  407 
  408         _exit(0);
  409     }
  410 
  411     return EXIT_SUCCESS;
  412 }
  413 
  414 stressor_info_t stress_clone_info = {
  415     .stressor = stress_clone,
  416     .class = CLASS_SCHEDULER | CLASS_OS,
  417     .opt_set_funcs = opt_set_funcs,
  418     .help = help
  419 };
  420 #else
  421 stressor_info_t stress_clone_info = {
  422     .stressor = stress_not_implemented,
  423     .class = CLASS_SCHEDULER | CLASS_OS,
  424     .opt_set_funcs = opt_set_funcs,
  425     .help = help
  426 };
  427 #endif