"Fossies" - the Fresh Open Source Software Archive

Member "stress-ng-0.09.56/stress-shm-sysv.c" (15 Mar 2019, 12484 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-shm-sysv.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 0.09.50_vs_0.09.51.

    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 #if defined(HAVE_SYS_IPC_H) &&  \
   28     defined(HAVE_SYS_SHM_H) &&  \
   29     defined(HAVE_SHM_SYSV)
   30 
   31 #define KEY_GET_RETRIES     (40)
   32 
   33 /*
   34  *  Note, running this test with the --maximize option on
   35  *  low memory systems with many instances can trigger the
   36  *  OOM killer fairly easily.  The test tries hard to reap
   37  *  reap shared memory segments that are left over if the
   38  *  child is killed, however if the OOM killer kills the
   39  *  parent that does the reaping, then one can be left with
   40  *  a system with many shared segments still reserved and
   41  *  little free memory.
   42  */
   43 typedef struct {
   44     int index;
   45     int shm_id;
   46 } shm_msg_t;
   47 
   48 static const int shm_flags[] = {
   49 #if defined(SHM_HUGETLB)
   50     SHM_HUGETLB,
   51 #endif
   52 #if defined(SHM_HUGE_2MB)
   53     SHM_HUGE_2MB,
   54 #endif
   55 #if defined(SHM_HUGE_1GB)
   56     SHM_HUGE_1GB,
   57 #endif
   58 /* This will segv if no backing, so don't use it for now */
   59 /*
   60 #if defined(SHM_NO_RESERVE)
   61     SHM_NO_RESERVE
   62 #endif
   63 */
   64     0
   65 };
   66 #endif
   67 
   68 int stress_set_shm_sysv_bytes(const char *opt)
   69 {
   70     size_t shm_sysv_bytes;
   71 
   72     shm_sysv_bytes = (size_t)get_uint64_byte(opt);
   73     check_range_bytes("shm-sysv-bytes", shm_sysv_bytes,
   74         MIN_SHM_SYSV_BYTES, MAX_MEM_LIMIT);
   75     return set_setting("shm-sysv-bytes", TYPE_ID_SIZE_T, &shm_sysv_bytes);
   76 }
   77 
   78 int stress_set_shm_sysv_segments(const char *opt)
   79 {
   80     size_t shm_sysv_segments;
   81 
   82     shm_sysv_segments = (size_t)get_uint64(opt);
   83     check_range("shm-sysv-segs", shm_sysv_segments,
   84         MIN_SHM_SYSV_SEGMENTS, MAX_SHM_SYSV_SEGMENTS);
   85     return set_setting("shm-sysv-segs", TYPE_ID_SIZE_T, &shm_sysv_segments);
   86 }
   87 
   88 #if defined(HAVE_SHM_SYSV)
   89 /*
   90  *  stress_shm_sysv_check()
   91  *  simple check if shared memory is sane
   92  */
   93 static int stress_shm_sysv_check(
   94     uint8_t *buf,
   95     const size_t sz,
   96     const size_t page_size)
   97 {
   98     uint8_t *ptr, *end = buf + sz;
   99     uint8_t val;
  100 
  101     for (val = 0, ptr = buf; ptr < end; ptr += page_size, val++) {
  102         *ptr = val;
  103     }
  104 
  105     for (val = 0, ptr = buf; ptr < end; ptr += page_size, val++) {
  106         if (*ptr != val)
  107             return -1;
  108 
  109     }
  110     return 0;
  111 }
  112 
  113 /*
  114  *  stress_shm_sysv_child()
  115  *  stress out the shm allocations. This can be killed by
  116  *  the out of memory killer, so we need to keep the parent
  117  *  informed of the allocated shared memory ids so these can
  118  *  be reaped cleanly if this process gets prematurely killed.
  119  */
  120 static int stress_shm_sysv_child(
  121     const args_t *args,
  122     const int fd,
  123     const size_t max_sz,
  124     const size_t page_size,
  125     const size_t shm_sysv_segments)
  126 {
  127     void *addrs[MAX_SHM_SYSV_SEGMENTS];
  128     key_t keys[MAX_SHM_SYSV_SEGMENTS];
  129     int shm_ids[MAX_SHM_SYSV_SEGMENTS];
  130     shm_msg_t msg;
  131     size_t i;
  132     int rc = EXIT_SUCCESS;
  133     bool ok = true;
  134     int mask = ~0;
  135     int32_t instances = args->num_instances;
  136 
  137     if (stress_sig_stop_stressing(args->name, SIGALRM) < 0)
  138         return EXIT_FAILURE;
  139 
  140     (void)memset(addrs, 0, sizeof(addrs));
  141     (void)memset(keys, 0, sizeof(keys));
  142     for (i = 0; i < MAX_SHM_SYSV_SEGMENTS; i++)
  143         shm_ids[i] = -1;
  144 
  145     /* Make sure this is killable by OOM killer */
  146     set_oom_adjustment(args->name, true);
  147 
  148     /* Should never happen, but be safe */
  149     if (instances < 1)
  150         instances = 1;
  151 
  152     do {
  153         size_t sz = max_sz;
  154 
  155         for (i = 0; i < shm_sysv_segments; i++) {
  156             int shm_id, count = 0;
  157             void *addr;
  158             key_t key;
  159             size_t shmall, freemem, totalmem, freeswap;
  160 
  161             /* Try hard not to overcommit at this current time */
  162             stress_get_memlimits(&shmall, &freemem, &totalmem, &freeswap);
  163             shmall /= instances;
  164             freemem /= instances;
  165             if ((shmall > page_size) && sz > shmall)
  166                 sz = shmall;
  167             if ((freemem > page_size) && sz > freemem)
  168                 sz = freemem;
  169             if (!g_keep_stressing_flag)
  170                 goto reap;
  171 
  172             for (count = 0; count < KEY_GET_RETRIES; count++) {
  173                 bool unique = true;
  174                 const int rnd =
  175                     mwc32() % SIZEOF_ARRAY(shm_flags);
  176                 const int rnd_flag = shm_flags[rnd] & mask;
  177 
  178                 if (sz < page_size)
  179                     goto reap;
  180 
  181                 /* Get a unique key */
  182                 do {
  183                     size_t j;
  184 
  185                     if (!g_keep_stressing_flag)
  186                         goto reap;
  187 
  188                     /* Get a unique random key */
  189                     key = (key_t)mwc16();
  190                     for (j = 0; j < i; j++) {
  191                         if (key == keys[j]) {
  192                             unique = false;
  193                             break;
  194                         }
  195                     }
  196                     if (!g_keep_stressing_flag)
  197                         goto reap;
  198 
  199                 } while (!unique);
  200 
  201                 shm_id = shmget(key, sz,
  202                     IPC_CREAT | IPC_EXCL |
  203                     S_IRUSR | S_IWUSR | rnd_flag);
  204                 if (shm_id >= 0)
  205                     break;
  206                 if (errno == EINTR)
  207                     goto reap;
  208                 if (errno == EPERM) {
  209                     /* ignore using the flag again */
  210                     mask &= ~rnd_flag;
  211                 }
  212                 if ((errno == EINVAL) || (errno == ENOMEM)) {
  213                     /*
  214                      * On some systems we may need
  215                      * to reduce the size
  216                      */
  217                     sz = sz / 2;
  218                 }
  219             }
  220             if (shm_id < 0) {
  221                 ok = false;
  222                 pr_fail_err("shmget");
  223                 rc = EXIT_FAILURE;
  224                 goto reap;
  225             }
  226 
  227             /* Inform parent of the new shm ID */
  228             msg.index = i;
  229             msg.shm_id = shm_id;
  230             if (write(fd, &msg, sizeof(msg)) < 0) {
  231                 pr_err("%s: write failed: errno=%d: (%s)\n",
  232                     args->name, errno, strerror(errno));
  233                 rc = EXIT_FAILURE;
  234                 goto reap;
  235             }
  236 
  237             addr = shmat(shm_id, NULL, 0);
  238             if (addr == (char *) -1) {
  239                 ok = false;
  240                 pr_fail_err("shmat");
  241                 rc = EXIT_FAILURE;
  242                 goto reap;
  243             }
  244             addrs[i] = addr;
  245             shm_ids[i] = shm_id;
  246             keys[i] = key;
  247 
  248             if (!keep_stressing())
  249                 goto reap;
  250             (void)mincore_touch_pages(addr, sz);
  251             (void)shim_msync(addr, sz, mwc1() ? MS_ASYNC : MS_SYNC);
  252 
  253             if (!keep_stressing())
  254                 goto reap;
  255             (void)madvise_random(addr, sz);
  256 
  257             if (!keep_stressing())
  258                 goto reap;
  259             if (stress_shm_sysv_check(addr, sz, page_size) < 0) {
  260                 ok = false;
  261                 pr_fail("%s: memory check failed\n", args->name);
  262                 rc = EXIT_FAILURE;
  263                 goto reap;
  264             }
  265 #if defined(SHM_LOCK) &&    \
  266     defined(SHM_UNLOCK)
  267             {
  268                 int ret;
  269 
  270                 ret = shmctl(shm_id, SHM_LOCK, NULL);
  271                 if (ret == 0) {
  272                     ret = shmctl(shm_id, SHM_UNLOCK, NULL);
  273                     (void)ret;
  274                 }
  275             }
  276 #endif
  277 #if defined(IPC_STAT) && \
  278     defined(HAVE_SHMID_DS)
  279             {
  280                 struct shmid_ds ds;
  281 
  282                 if (shmctl(shm_id, IPC_STAT, &ds) < 0)
  283                     pr_fail_dbg("shmctl IPC_STAT");
  284 #if defined(SHM_SET)
  285                 else {
  286                     int ret;
  287 
  288                     ret = shmctl(shm_id, SHM_SET, &ds);
  289                     (void)ret;
  290                 }
  291 #endif
  292             }
  293 #endif
  294 #if defined(IPC_INFO) && \
  295     defined(HAVE_SHMINFO)
  296             {
  297                 struct shminfo s;
  298 
  299                 if (shmctl(shm_id, IPC_INFO, (struct shmid_ds *)&s) < 0)
  300                     pr_fail_dbg("semctl IPC_INFO");
  301             }
  302 #endif
  303 #if defined(SHM_INFO) && \
  304     defined(HAVE_SHMINFO)
  305             {
  306                 struct shm_info s;
  307 
  308                 if (shmctl(shm_id, SHM_INFO, (struct shmid_ds *)&s) < 0)
  309                     pr_fail_dbg("semctl SHM_INFO");
  310             }
  311 #endif
  312             inc_counter(args);
  313         }
  314 reap:
  315         for (i = 0; i < shm_sysv_segments; i++) {
  316             if (addrs[i]) {
  317                 if (shmdt(addrs[i]) < 0) {
  318                     pr_fail_err("shmdt");
  319                 }
  320             }
  321             if (shm_ids[i] >= 0) {
  322                 if (shmctl(shm_ids[i], IPC_RMID, NULL) < 0) {
  323                     if (errno != EIDRM)
  324                         pr_fail_err("shmctl");
  325                 }
  326             }
  327 
  328             /* Inform parent shm ID is now free */
  329             msg.index = i;
  330             msg.shm_id = -1;
  331             if (write(fd, &msg, sizeof(msg)) < 0) {
  332                 pr_dbg("%s: write failed: errno=%d: (%s)\n",
  333                     args->name, errno, strerror(errno));
  334                 ok = false;
  335             }
  336             addrs[i] = NULL;
  337             shm_ids[i] = -1;
  338             keys[i] = 0;
  339         }
  340     } while (ok && keep_stressing());
  341 
  342     /* Inform parent of end of run */
  343     msg.index = -1;
  344     msg.shm_id = -1;
  345     if (write(fd, &msg, sizeof(msg)) < 0) {
  346         pr_err("%s: write failed: errno=%d: (%s)\n",
  347             args->name, errno, strerror(errno));
  348         rc = EXIT_FAILURE;
  349     }
  350 
  351     return rc;
  352 }
  353 
  354 /*
  355  *  stress_shm_sysv()
  356  *  stress SYSTEM V shared memory
  357  */
  358 static int stress_shm_sysv(const args_t *args)
  359 {
  360     const size_t page_size = args->page_size;
  361     size_t orig_sz, sz;
  362     int pipefds[2];
  363     int rc = EXIT_SUCCESS;
  364     ssize_t i;
  365     pid_t pid;
  366     bool retry = true;
  367     uint32_t restarts = 0;
  368     size_t shm_sysv_bytes = DEFAULT_SHM_SYSV_BYTES;
  369     size_t shm_sysv_segments = DEFAULT_SHM_SYSV_SEGMENTS;
  370 
  371     if (!get_setting("shm-sysv-bytes", &shm_sysv_bytes)) {
  372         if (g_opt_flags & OPT_FLAGS_MAXIMIZE)
  373             shm_sysv_bytes = MAX_SHM_SYSV_BYTES;
  374         if (g_opt_flags & OPT_FLAGS_MINIMIZE)
  375             shm_sysv_bytes = MIN_SHM_SYSV_BYTES;
  376     }
  377     if (shm_sysv_bytes < page_size)
  378         shm_sysv_bytes = page_size;
  379 
  380     if (!get_setting("shm-sysv-segs", &shm_sysv_segments)) {
  381         if (g_opt_flags & OPT_FLAGS_MAXIMIZE)
  382             shm_sysv_segments = MAX_SHM_SYSV_SEGMENTS;
  383         if (g_opt_flags & OPT_FLAGS_MINIMIZE)
  384             shm_sysv_segments = MIN_SHM_SYSV_SEGMENTS;
  385     }
  386     shm_sysv_segments /= args->num_instances;
  387     if (shm_sysv_segments < 1)
  388         shm_sysv_segments = 1;
  389 
  390     orig_sz = sz = shm_sysv_bytes & ~(page_size - 1);
  391 
  392     while (g_keep_stressing_flag && retry) {
  393         if (pipe(pipefds) < 0) {
  394             pr_fail_dbg("pipe");
  395             return EXIT_FAILURE;
  396         }
  397 fork_again:
  398         pid = fork();
  399         if (pid < 0) {
  400             /* Can't fork, retry? */
  401             if (errno == EAGAIN)
  402                 goto fork_again;
  403             pr_err("%s: fork failed: errno=%d: (%s)\n",
  404                 args->name, errno, strerror(errno));
  405             (void)close(pipefds[0]);
  406             (void)close(pipefds[1]);
  407 
  408             /* Nope, give up! */
  409             return EXIT_FAILURE;
  410         } else if (pid > 0) {
  411             /* Parent */
  412             int status, shm_ids[MAX_SHM_SYSV_SEGMENTS];
  413 
  414             (void)setpgid(pid, g_pgrp);
  415             set_oom_adjustment(args->name, false);
  416             (void)close(pipefds[1]);
  417 
  418             for (i = 0; i < (ssize_t)MAX_SHM_SYSV_SEGMENTS; i++)
  419                 shm_ids[i] = -1;
  420 
  421             while (g_keep_stressing_flag) {
  422                 shm_msg_t   msg;
  423                 ssize_t n;
  424 
  425                 /*
  426                  *  Blocking read on child shm ID info
  427                  *  pipe.  We break out if pipe breaks
  428                  *  on child death, or child tells us
  429                  *  about its demise.
  430                  */
  431                 n = read(pipefds[0], &msg, sizeof(msg));
  432                 if (n <= 0) {
  433                     if ((errno == EAGAIN) || (errno == EINTR))
  434                         continue;
  435                     if (errno) {
  436                         pr_fail_dbg("read");
  437                         break;
  438                     }
  439                     pr_fail_dbg("zero byte read");
  440                     break;
  441                 }
  442                 if ((msg.index < 0) ||
  443                     (msg.index >= MAX_SHM_SYSV_SEGMENTS)) {
  444                     retry = false;
  445                     break;
  446                 }
  447                 shm_ids[msg.index] = msg.shm_id;
  448             }
  449             (void)kill(pid, SIGALRM);
  450             (void)waitpid(pid, &status, 0);
  451             if (WIFSIGNALED(status)) {
  452                 if ((WTERMSIG(status) == SIGKILL) ||
  453                     (WTERMSIG(status) == SIGBUS)) {
  454                     log_system_mem_info();
  455                     pr_dbg("%s: assuming killed by OOM killer, "
  456                         "restarting again (instance %d)\n",
  457                         args->name, args->instance);
  458                     restarts++;
  459                 }
  460             }
  461             (void)close(pipefds[0]);
  462             /*
  463              *  The child may have been killed by the OOM killer or
  464              *  some other way, so it may have left the shared
  465              *  memory segment around.  At this point the child
  466              *  has died, so we should be able to remove the
  467              *  shared memory segment.
  468              */
  469             for (i = 0; i < (ssize_t)shm_sysv_segments; i++) {
  470                 if (shm_ids[i] != -1)
  471                     (void)shmctl(shm_ids[i], IPC_RMID, NULL);
  472             }
  473         } else if (pid == 0) {
  474             /* Child, stress memory */
  475             (void)setpgid(0, g_pgrp);
  476             stress_parent_died_alarm();
  477 
  478             /*
  479              * Nicing the child may OOM it first as this
  480              * doubles the OOM score
  481              */
  482             if (nice(5) < 0)
  483                 pr_dbg("%s: nice of child failed, "
  484                     "(instance %d)\n", args->name, args->instance);
  485 
  486             (void)close(pipefds[0]);
  487             rc = stress_shm_sysv_child(args, pipefds[1], sz, page_size, shm_sysv_segments);
  488             (void)close(pipefds[1]);
  489             _exit(rc);
  490         }
  491     }
  492     if (orig_sz != sz)
  493         pr_dbg("%s: reduced shared memory size from "
  494             "%zu to %zu bytes\n", args->name, orig_sz, sz);
  495     if (restarts) {
  496         pr_dbg("%s: OOM restarts: %" PRIu32 "\n",
  497             args->name, restarts);
  498     }
  499     return rc;
  500 }
  501 
  502 stressor_info_t stress_shm_sysv_info = {
  503     .stressor = stress_shm_sysv,
  504     .class = CLASS_VM | CLASS_OS
  505 };
  506 #else
  507 stressor_info_t stress_shm_sysv_info = {
  508     .stressor = stress_not_implemented,
  509     .class = CLASS_VM | CLASS_OS
  510 };
  511 #endif