"Fossies" - the Fresh Open Source Software Archive

Member "stress-ng-0.09.56/stress-tmpfs.c" (15 Mar 2019, 9946 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-tmpfs.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) 2017-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_VFS_H) && \
   28     defined(HAVE_STATFS)
   29 
   30 #define MAX_MOUNTS      (256)
   31 #define NO_MEM_RETRIES_MAX  (256)
   32 #define TMPFS_MAGIC     0x01021994
   33 
   34 /* Misc randomly chosen mmap flags */
   35 static const int mmap_flags[] = {
   36 #if defined(MAP_HUGE_2MB) && defined(MAP_HUGETLB)
   37     MAP_HUGE_2MB | MAP_HUGETLB,
   38 #endif
   39 #if defined(MAP_HUGE_1GB) && defined(MAP_HUGETLB)
   40     MAP_HUGE_1GB | MAP_HUGETLB,
   41 #endif
   42 #if defined(MAP_NONBLOCK)
   43     MAP_NONBLOCK,
   44 #endif
   45 #if defined(MAP_LOCKED)
   46     MAP_LOCKED,
   47 #endif
   48     0
   49 };
   50 
   51 /*
   52  *  stress_tmpfs_open()
   53  *  attempts to find a writeable tmpfs file system and opens
   54  *  a tmpfs temp file. The file is unlinked so the final close
   55  *  will enforce and automatic space reap if the child process
   56  *  exits prematurely.
   57  */
   58 static int stress_tmpfs_open(const args_t *args, off_t *len)
   59 {
   60     const uint32_t rnd = mwc32();
   61     char path[PATH_MAX];
   62     char *mnts[MAX_MOUNTS];
   63     int i, n, fd = -1;
   64 
   65     (void)memset(mnts, 0, sizeof(mnts));
   66 
   67     *len = 0;
   68     n = mount_get(mnts, SIZEOF_ARRAY(mnts));
   69     if (n < 0)
   70         return -1;
   71 
   72     for (i = 0; i < n; i++) {
   73         struct statfs buf;
   74 
   75         if (!mnts[i])
   76             continue;
   77         /* Some paths should be avoided... */
   78         if (!strncmp(mnts[i], "/dev", 4))
   79             continue;
   80         if (!strncmp(mnts[i], "/sys", 4))
   81             continue;
   82         if (!strncmp(mnts[i], "/run/lock", 9))
   83             continue;
   84         if (statfs(mnts[i], &buf) < 0)
   85             continue;
   86 
   87         /* ..and must be TMPFS too.. */
   88         if (buf.f_type != TMPFS_MAGIC)
   89             continue;
   90 
   91         /* We have a candidate, try to create a tmpfs file */
   92         (void)snprintf(path, sizeof(path), "%s/%s-%d-%" PRIu32 "-%" PRIu32,
   93             mnts[i], args->name, args->pid, args->instance, rnd);
   94         fd = open(path, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
   95         if (fd >= 0) {
   96             int rc;
   97             const char data = 0;
   98             off_t max_size = buf.f_bsize * buf.f_bavail;
   99 
  100             /*
  101              * Don't use all the tmpfs, just 98% for all instance
  102              */
  103             max_size = (max_size * 98) / 100;
  104             max_size /= args->num_instances;
  105 
  106             (void)unlink(path);
  107             /*
  108              *  make file with hole; we want this
  109              *  to be autopopulated with pages
  110              *  over time
  111              */
  112             rc = lseek(fd, max_size, SEEK_SET);
  113             if (rc < 0) {
  114                 (void)close(fd);
  115                 continue;
  116             }
  117             rc = write(fd, &data, sizeof(data));
  118             if (rc < 0) {
  119                 (void)close(fd);
  120                 continue;
  121             }
  122             *len = max_size;
  123             break;
  124         }
  125     }
  126     mount_free(mnts, n);
  127 
  128     return fd;
  129 }
  130 
  131 static void stress_tmpfs_child(
  132     const args_t *args,
  133     const int fd,
  134     int *flags,
  135     const size_t page_size,
  136     const size_t sz,
  137     const size_t pages4k)
  138 {
  139     int no_mem_retries = 0;
  140     const int ms_flags = (g_opt_flags & OPT_FLAGS_MMAP_ASYNC) ?
  141         MS_ASYNC : MS_SYNC;
  142 
  143     do {
  144         uint8_t mapped[pages4k];
  145         uint8_t *mappings[pages4k];
  146         size_t n;
  147         const int rnd = mwc32() % SIZEOF_ARRAY(mmap_flags);
  148         const int rnd_flag = mmap_flags[rnd];
  149         uint8_t *buf = NULL;
  150 
  151         if (no_mem_retries >= NO_MEM_RETRIES_MAX) {
  152             pr_err("%s: gave up trying to mmap, no available memory\n",
  153                 args->name);
  154             break;
  155         }
  156 
  157         if (!g_keep_stressing_flag)
  158             break;
  159         buf = (uint8_t *)mmap(NULL, sz,
  160             PROT_READ | PROT_WRITE, *flags | rnd_flag, fd, 0);
  161         if (buf == MAP_FAILED) {
  162             /* Force MAP_POPULATE off, just in case */
  163 #if defined(MAP_POPULATE)
  164             *flags &= ~MAP_POPULATE;
  165 #endif
  166             no_mem_retries++;
  167             if (no_mem_retries > 1)
  168                 (void)shim_usleep(100000);
  169             continue;   /* Try again */
  170         }
  171         if (g_opt_flags & OPT_FLAGS_MMAP_FILE) {
  172             (void)memset(buf, 0xff, sz);
  173             (void)shim_msync((void *)buf, sz, ms_flags);
  174         }
  175         (void)madvise_random(buf, sz);
  176         (void)mincore_touch_pages(buf, sz);
  177         (void)memset(mapped, PAGE_MAPPED, sizeof(mapped));
  178         for (n = 0; n < pages4k; n++)
  179             mappings[n] = buf + (n * page_size);
  180 
  181         /* Ensure we can write to the mapped pages */
  182         mmap_set(buf, sz, page_size);
  183         if (g_opt_flags & OPT_FLAGS_VERIFY) {
  184             if (mmap_check(buf, sz, page_size) < 0)
  185                 pr_fail("%s: mmap'd region of %zu bytes does "
  186                     "not contain expected data\n", args->name, sz);
  187         }
  188 
  189         /*
  190          *  Step #1, unmap all pages in random order
  191          */
  192         (void)mincore_touch_pages(buf, sz);
  193         for (n = pages4k; n; ) {
  194             uint64_t j, i = mwc64() % pages4k;
  195             for (j = 0; j < n; j++) {
  196                 uint64_t page = (i + j) % pages4k;
  197                 if (mapped[page] == PAGE_MAPPED) {
  198                     mapped[page] = 0;
  199                     (void)madvise_random(mappings[page], page_size);
  200                     (void)munmap((void *)mappings[page], page_size);
  201                     n--;
  202                     break;
  203                 }
  204                 if (!g_keep_stressing_flag)
  205                     goto cleanup;
  206             }
  207         }
  208         (void)munmap((void *)buf, sz);
  209 #if defined(MAP_FIXED)
  210         /*
  211          *  Step #2, map them back in random order
  212          */
  213         for (n = pages4k; n; ) {
  214             uint64_t j, i = mwc64() % pages4k;
  215             for (j = 0; j < n; j++) {
  216                 uint64_t page = (i + j) % pages4k;
  217                 if (!mapped[page]) {
  218                     off_t offset = (g_opt_flags & OPT_FLAGS_MMAP_FILE) ?
  219                             page * page_size : 0;
  220                     /*
  221                      * Attempt to map them back into the original address, this
  222                      * may fail (it's not the most portable operation), so keep
  223                      * track of failed mappings too
  224                      */
  225                     mappings[page] = (uint8_t *)mmap((void *)mappings[page],
  226                         page_size, PROT_READ | PROT_WRITE, MAP_FIXED | *flags, fd, offset);
  227                     if (mappings[page] == MAP_FAILED) {
  228                         mapped[page] = PAGE_MAPPED_FAIL;
  229                         mappings[page] = NULL;
  230                     } else {
  231                         (void)mincore_touch_pages(mappings[page], page_size);
  232                         (void)madvise_random(mappings[page], page_size);
  233                         mapped[page] = PAGE_MAPPED;
  234                         /* Ensure we can write to the mapped page */
  235                         mmap_set(mappings[page], page_size, page_size);
  236                         if (mmap_check(mappings[page], page_size, page_size) < 0)
  237                             pr_fail("%s: mmap'd region of %zu bytes does "
  238                                 "not contain expected data\n", args->name, page_size);
  239                         if (g_opt_flags & OPT_FLAGS_MMAP_FILE) {
  240                             (void)memset(mappings[page], n, page_size);
  241                             (void)shim_msync((void *)mappings[page], page_size, ms_flags);
  242                         }
  243                     }
  244                     n--;
  245                     break;
  246                 }
  247                 if (!g_keep_stressing_flag)
  248                     goto cleanup;
  249             }
  250         }
  251 #endif
  252 cleanup:
  253         /*
  254          *  Step #3, unmap them all
  255          */
  256         for (n = 0; n < pages4k; n++) {
  257             if (mapped[n] & PAGE_MAPPED) {
  258                 (void)madvise_random(mappings[n], page_size);
  259                 (void)munmap((void *)mappings[n], page_size);
  260             }
  261         }
  262         inc_counter(args);
  263     } while (keep_stressing());
  264 }
  265 
  266 /*
  267  *  stress_tmpfs()
  268  *  stress tmpfs
  269  */
  270 static int stress_tmpfs(const args_t *args)
  271 {
  272     const size_t page_size = args->page_size;
  273     off_t sz;
  274     size_t pages4k;
  275     pid_t pid;
  276     int fd, flags = MAP_SHARED;
  277     uint32_t ooms = 0, segvs = 0, buserrs = 0;
  278 
  279 #if defined(MAP_POPULATE)
  280     flags |= MAP_POPULATE;
  281 #endif
  282     fd = stress_tmpfs_open(args, &sz);
  283     if (fd < 0) {
  284         pr_err("%s: cannot find writeable free space on a "
  285             "tmpfs filesystem\n", args->name);
  286         return EXIT_NO_RESOURCE;
  287     }
  288     pages4k = (size_t)sz / page_size;
  289 
  290     /* Make sure this is killable by OOM killer */
  291     set_oom_adjustment(args->name, true);
  292 
  293 again:
  294     if (!g_keep_stressing_flag)
  295         goto cleanup;
  296     pid = fork();
  297     if (pid < 0) {
  298         if (errno == EAGAIN)
  299             goto again;
  300         pr_err("%s: fork failed: errno=%d: (%s)\n",
  301             args->name, errno, strerror(errno));
  302     } else if (pid > 0) {
  303         int status, ret;
  304 
  305         (void)setpgid(pid, g_pgrp);
  306         /* Parent, wait for child */
  307         ret = waitpid(pid, &status, 0);
  308         if (ret < 0) {
  309             if (errno != EINTR)
  310                 pr_dbg("%s: waitpid(): errno=%d (%s)\n",
  311                     args->name, errno, strerror(errno));
  312             (void)kill(pid, SIGTERM);
  313             (void)kill(pid, SIGKILL);
  314             (void)waitpid(pid, &status, 0);
  315         } else if (WIFSIGNALED(status)) {
  316             /* If we got killed by sigbus, re-start */
  317             if (WTERMSIG(status) == SIGBUS) {
  318                 /* Happens frequently, so be silent */
  319                 buserrs++;
  320                 goto again;
  321             }
  322 
  323             pr_dbg("%s: child died: %s (instance %d)\n",
  324                 args->name, stress_strsignal(WTERMSIG(status)),
  325                 args->instance);
  326             /* If we got killed by OOM killer, re-start */
  327             if (WTERMSIG(status) == SIGKILL) {
  328                 log_system_mem_info();
  329                 pr_dbg("%s: assuming killed by OOM "
  330                     "killer, restarting again "
  331                     "(instance %d)\n",
  332                     args->name, args->instance);
  333                 ooms++;
  334                 goto again;
  335             }
  336             /* If we got killed by sigsegv, re-start */
  337             if (WTERMSIG(status) == SIGSEGV) {
  338                 pr_dbg("%s: killed by SIGSEGV, "
  339                     "restarting again "
  340                     "(instance %d)\n",
  341                     args->name, args->instance);
  342                 segvs++;
  343                 goto again;
  344             }
  345         }
  346     } else if (pid == 0) {
  347         (void)setpgid(0, g_pgrp);
  348         stress_parent_died_alarm();
  349 
  350         /* Make sure this is killable by OOM killer */
  351         set_oom_adjustment(args->name, true);
  352 
  353         stress_tmpfs_child(args, fd, &flags, page_size, sz, pages4k);
  354     }
  355 
  356 cleanup:
  357     (void)close(fd);
  358     if (ooms + segvs + buserrs > 0)
  359         pr_dbg("%s: OOM restarts: %" PRIu32
  360             ", SEGV restarts: %" PRIu32
  361             ", SIGBUS signals: %" PRIu32 "\n",
  362             args->name, ooms, segvs, buserrs);
  363 
  364     return EXIT_SUCCESS;
  365 }
  366 stressor_info_t stress_tmpfs_info = {
  367     .stressor = stress_tmpfs,
  368     .class = CLASS_MEMORY | CLASS_VM | CLASS_OS
  369 };
  370 #else
  371 stressor_info_t stress_tmpfs_info = {
  372     .stressor = stress_not_implemented,
  373     .class = CLASS_MEMORY | CLASS_VM | CLASS_OS
  374 };
  375 #endif