"Fossies" - the Fresh Open Source Software Archive

Member "stress-ng-0.09.56/stress-sysfs.c" (15 Mar 2019, 11752 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-sysfs.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  * This program is free software; you can redistribute it and/or
    3  * modify it under the terms of the GNU General Public License
    4  * as published by the Free Software Foundation; either version 2
    5  * of the License, or (at your option) any later version.
    6  *
    7  * This program is distributed in the hope that it will be useful,
    8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   10  * GNU General Public License for more details.
   11  *
   12  * You should have received a copy of the GNU General Public License
   13  * along with this program; if not, write to the Free Software
   14  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
   15  *
   16  * This code is a complete clean re-write of the stress tool by
   17  * Colin Ian King <colin.king@canonical.com> and attempts to be
   18  * backwardly compatible with the stress tool by Amos Waterland
   19  * <apw@rossby.metr.ou.edu> but has more stress tests and more
   20  * functionality.
   21  *
   22  */
   23 #include "stress-ng.h"
   24 
   25 #if defined(HAVE_LIB_PTHREAD) && defined(__linux__)
   26 
   27 #define SYS_BUF_SZ      (4096)
   28 #define MAX_READ_THREADS    (4) /* threads stressing sysfs */
   29 #define DRAIN_DELAY_US      (50000) /* backoff in (us) microsecs */
   30 #define DURATION_PER_SYSFS_FILE (40000) /* max duration per file in microsecs */
   31 #define OPS_PER_SYSFS_FILE  (256)   /* max iterations per sysfs file */
   32 
   33 static sigset_t set;
   34 static shim_pthread_spinlock_t lock;
   35 static char *sysfs_path;
   36 static uint32_t mixup;
   37 static volatile bool segv_abort = false;
   38 static volatile bool drain_kmsg = false;
   39 static volatile bool usr2_killed = false;
   40 static volatile uint32_t counter = 0;
   41 static sigjmp_buf jmp_env;
   42 static char signum_path[] = "/sys/kernel/notes";
   43 static uint32_t os_release;
   44 
   45 typedef struct ctxt {
   46     const args_t *args;     /* stressor args */
   47     char *badbuf;           /* bad mapping for I/O buffer */
   48     int kmsgfd;         /* /dev/kmsg file descriptor */
   49     bool writeable;         /* is sysfs writeable? */
   50 } ctxt_t;
   51 
   52 static void MLOCKED_TEXT stress_segv_handler(int signum)
   53 {
   54     (void)signum;
   55 
   56     segv_abort = true;
   57     siglongjmp(jmp_env, 1);
   58 }
   59 
   60 static void MLOCKED_TEXT stress_usr2_handler(int signum)
   61 {
   62     (void)signum;
   63 }
   64 
   65 static uint32_t path_sum(const char *path)
   66 {
   67     const char *ptr = path;
   68     register uint32_t sum = mixup;
   69 
   70     while (*ptr) {
   71         sum <<= 1;
   72         sum += *(ptr++);
   73     }
   74 
   75     return sum;
   76 }
   77 
   78 static int mixup_sort(const struct dirent **d1, const struct dirent **d2)
   79 {
   80     uint32_t s1, s2;
   81 
   82     s1 = path_sum((*d1)->d_name);
   83     s2 = path_sum((*d2)->d_name);
   84 
   85     if (s1 == s2)
   86         return 0;
   87     return (s1 < s2) ? -1 : 1;
   88 }
   89 
   90 /*
   91  *  stress_kmsg_drain()
   92  *  drain message buffer, return true if we need
   93  *  to drain lots of backed up messages because
   94  *  the stressor is spamming the kmsg log
   95  */
   96 static bool stress_kmsg_drain(const int fd)
   97 {
   98     int count = 0;
   99 
  100     if (fd == -1)
  101         return false;
  102 
  103     for (;;) {
  104         ssize_t ret;
  105         char buffer[1024];
  106 
  107         ret = read(fd, buffer, sizeof(buffer));
  108         if (ret <= 0)
  109             break;
  110 
  111         count += ret;
  112     }
  113     return count > 0;
  114 }
  115 
  116 /*
  117  *  stress_sys_rw()
  118  *  read a proc file
  119  */
  120 static inline bool stress_sys_rw(const ctxt_t *ctxt)
  121 {
  122     int fd;
  123     ssize_t i = 0, ret;
  124     char buffer[SYS_BUF_SZ];
  125     char path[PATH_MAX];
  126     const args_t *args = ctxt->args;
  127     const double threshold = 0.2;
  128 
  129     while (g_keep_stressing_flag && !segv_abort) {
  130         double t_start;
  131 
  132         ret = shim_pthread_spin_lock(&lock);
  133         if (ret)
  134             return false;
  135         (void)shim_strlcpy(path, sysfs_path, sizeof(path));
  136         counter++;
  137         if (counter > OPS_PER_SYSFS_FILE)
  138             (void)kill(args->pid, SIGUSR2);
  139         (void)shim_pthread_spin_unlock(&lock);
  140 
  141         if (!*path || !g_keep_stressing_flag)
  142             break;
  143 
  144         t_start = time_now();
  145         if ((fd = open(path, O_RDONLY | O_NONBLOCK)) < 0)
  146             goto next;
  147         if (time_now() - t_start > threshold) {
  148             (void)close(fd);
  149             goto next;
  150         }
  151 
  152         /*
  153          *  Multiple randomly sized reads
  154          */
  155         while (i < (4096 * SYS_BUF_SZ)) {
  156             ssize_t sz = 1 + (mwc32() % (sizeof(buffer) - 1));
  157             if (!g_keep_stressing_flag)
  158                 break;
  159             ret = read(fd, buffer, sz);
  160             if (ret < 0)
  161                 break;
  162             if (ret < sz)
  163                 break;
  164             i += sz;
  165 
  166             if (stress_kmsg_drain(ctxt->kmsgfd)) {
  167                 drain_kmsg = true;
  168                 (void)close(fd);
  169                 goto drain;
  170             }
  171             if (time_now() - t_start > threshold) {
  172                 (void)close(fd);
  173                 goto next;
  174             }
  175         }
  176 
  177         /* file stat should be OK if we've just opened it */
  178         if (g_opt_flags & OPT_FLAGS_VERIFY) {
  179             struct stat buf;
  180 
  181             if (fstat(fd, &buf) < 0) {
  182                 pr_fail_err("stat");
  183             } else {
  184 #if 0
  185                 if ((buf.st_mode & S_IROTH) == 0) {
  186                     pr_fail("%s: read access failed on %s which "
  187                         "could be opened, errno=%d (%s)\n",
  188                     args->name, path, errno, strerror(errno));
  189                 }
  190 #endif
  191             }
  192         }
  193         (void)close(fd);
  194 
  195         if (time_now() - t_start > threshold)
  196             goto next;
  197         if ((fd = open(path, O_RDONLY | O_NONBLOCK)) < 0)
  198             goto next;
  199         /*
  200          *  Zero sized reads
  201          */
  202         ret = read(fd, buffer, 0);
  203         if (ret < 0)
  204             goto err;
  205         if (time_now() - t_start > threshold)
  206             goto next;
  207         if (stress_kmsg_drain(ctxt->kmsgfd)) {
  208             drain_kmsg = true;
  209             (void)close(fd);
  210             goto drain;
  211         }
  212 
  213         /*
  214          *  Bad read buffer
  215          */
  216         if (ctxt->badbuf) {
  217             ret = read(fd, ctxt->badbuf, SYS_BUF_SZ);
  218             if (ret < 0)
  219                 goto err;
  220         }
  221         if (stress_kmsg_drain(ctxt->kmsgfd)) {
  222             drain_kmsg = true;
  223             (void)close(fd);
  224             goto drain;
  225         }
  226 err:
  227         (void)close(fd);
  228         if (time_now() - t_start > threshold)
  229             goto next;
  230 
  231         /*
  232          *  We only attempt writes if we are not
  233          *  root
  234          */
  235         if (ctxt->writeable) {
  236             /*
  237              *  Zero sized writes
  238              */
  239             if ((fd = open(path, O_WRONLY | O_NONBLOCK)) < 0)
  240                 goto next;
  241             ret = write(fd, buffer, 0);
  242             (void)ret;
  243             (void)close(fd);
  244 
  245             if (time_now() - t_start > threshold)
  246                 goto next;
  247         }
  248 next:
  249         if (stress_kmsg_drain(ctxt->kmsgfd)) {
  250             drain_kmsg = true;
  251             goto drain;
  252         }
  253 
  254         if (drain_kmsg) {
  255 drain:
  256             (void)shim_usleep(DRAIN_DELAY_US);
  257             continue;
  258         }
  259     }
  260     return false;
  261 }
  262 
  263 /*
  264  *  stress_sys_rw_thread
  265  *  keep exercising a sysfs entry until
  266  *  controlling thread triggers an exit
  267  */
  268 static void *stress_sys_rw_thread(void *ctxt_ptr)
  269 {
  270     static void *nowt = NULL;
  271     uint8_t stack[SIGSTKSZ + STACK_ALIGNMENT];
  272     ctxt_t *ctxt = (ctxt_t *)ctxt_ptr;
  273 
  274     /*
  275      *  Block all signals, let controlling thread
  276      *  handle these
  277      */
  278     (void)sigprocmask(SIG_BLOCK, &set, NULL);
  279 
  280     /*
  281      *  According to POSIX.1 a thread should have
  282      *  a distinct alternative signal stack.
  283      *  However, we block signals in this thread
  284      *  so this is probably just totally unncessary.
  285      */
  286     (void)memset(stack, 0, sizeof(stack));
  287     if (stress_sigaltstack(stack, SIGSTKSZ) < 0)
  288         return &nowt;
  289 
  290     while (g_keep_stressing_flag && !segv_abort)
  291         stress_sys_rw(ctxt);
  292 
  293     return &nowt;
  294 }
  295 
  296 /*
  297  *  stress_sys_skip()
  298  *  skip paths that are known to cause issues
  299  */
  300 static bool stress_sys_skip(const char *path)
  301 {
  302     /*
  303      *  Can OOPS on Azure when reading
  304      *  "/sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A03:00/device:07/" \
  305      *  "VMBUS:01/99221fa0-24ad-11e2-be98-001aa01bbf6e/channels/4/read_avail"
  306      */
  307     if (strstr(path, "PNP0A03") && strstr(path, "VMBUS"))
  308         return true;
  309     /*
  310      *  Has been known to cause issues on s390x
  311      *
  312     if (strstr(path, "virtio0/block") && strstr(path, "cache_type"))
  313         return true;
  314      */
  315 
  316     /*
  317      *  The tpm driver for pre Linux 4.10 is racey so skip
  318      */
  319     if ((os_release < 410) && (strstr(path, "/sys/kernel/security/tpm0")))
  320         return true;
  321 
  322     return false;
  323 }
  324 
  325 /*
  326  *  stress_sys_dir()
  327  *  read directory
  328  */
  329 static void stress_sys_dir(
  330     const ctxt_t *ctxt,
  331     const char *path,
  332     const bool recurse,
  333     const int depth)
  334 {
  335     struct dirent **dlist;
  336     const args_t *args = ctxt->args;
  337     const mode_t flags = S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
  338     int i, n;
  339 
  340     if (!g_keep_stressing_flag || segv_abort)
  341         return;
  342 
  343     /* Don't want to go too deep */
  344     if (depth > 20)
  345         return;
  346 
  347     mixup = mwc32();
  348     dlist = NULL;
  349     n = scandir(path, &dlist, NULL, mixup_sort);
  350     if (n <= 0)
  351         goto done;
  352 
  353     for (i = 0; (i < n) && !segv_abort; i++) {
  354         int ret;
  355         struct stat buf;
  356         char filename[PATH_MAX];
  357         char tmp[PATH_MAX];
  358         struct dirent *d = dlist[i];
  359 
  360         if (!keep_stressing())
  361             break;
  362         if (stress_is_dot_filename(d->d_name))
  363             continue;
  364 
  365         (void)snprintf(tmp, sizeof(tmp), "%s/%s", path, d->d_name);
  366         if (stress_sys_skip(tmp))
  367             continue;
  368 
  369         switch (d->d_type) {
  370         case DT_DIR:
  371             if (!recurse)
  372                 continue;
  373 
  374             ret = stat(tmp, &buf);
  375             if (ret < 0)
  376                 continue;
  377             if ((buf.st_mode & flags) == 0)
  378                 continue;
  379 
  380             inc_counter(args);
  381             stress_sys_dir(ctxt, tmp, recurse, depth + 1);
  382             break;
  383         case DT_REG:
  384             ret = stat(tmp, &buf);
  385             if (ret < 0)
  386                 continue;
  387 
  388             if ((buf.st_mode & flags) == 0)
  389                 continue;
  390 
  391             ret = shim_pthread_spin_lock(&lock);
  392             if (!ret) {
  393                 (void)shim_strlcpy(filename, tmp, sizeof(filename));
  394                 sysfs_path = filename;
  395                 counter = 0;
  396                 usr2_killed = false;
  397                 (void)shim_pthread_spin_unlock(&lock);
  398                 drain_kmsg = false;
  399 
  400                 /*
  401                  *  wait for a timeout, or until woken up
  402                  *  by pthread(s) once maximum iteration count
  403                  *  has been reached
  404                  */
  405                 shim_usleep_interruptible(DURATION_PER_SYSFS_FILE);
  406                 if (segv_abort)
  407                     break;
  408                 inc_counter(args);
  409             }
  410             break;
  411         default:
  412             break;
  413         }
  414     }
  415 done:
  416     if (dlist) {
  417         for (i = 0; i < n; i++)
  418             free(dlist[i]);
  419         free(dlist);
  420     }
  421 }
  422 
  423 
  424 /*
  425  *  stress_sysfs
  426  *  stress reading all of /sys
  427  */
  428 static int stress_sysfs(const args_t *args)
  429 {
  430     size_t i;
  431     pthread_t pthreads[MAX_READ_THREADS];
  432     int rc, ret[MAX_READ_THREADS];
  433     ctxt_t ctxt;
  434 
  435     os_release = 0;
  436 #if defined(HAVE_UNAME) && defined(HAVE_SYS_UTSNAME_H)
  437     {
  438         static struct utsname utsbuf;
  439 
  440         rc = uname(&utsbuf);
  441         if (rc == 0) {
  442             uint16_t major, minor;
  443 
  444             if (sscanf(utsbuf.release, "%5" SCNd16 ".%5" SCNd16, &major, &minor) == 2)
  445                 os_release = (major * 100) + minor;
  446         }
  447     }
  448 #endif
  449 
  450     (void)memset(&ctxt, 0, sizeof(ctxt));
  451     rc = sigsetjmp(jmp_env, 1);
  452     if (rc) {
  453         pr_err("%s: A SIGSEGV occurred while exercising %s, aborting\n",
  454             args->name, sysfs_path);
  455         return EXIT_FAILURE;
  456     }
  457     if (stress_sighandler(args->name, SIGSEGV, stress_segv_handler, NULL) < 0)
  458         return EXIT_FAILURE;
  459     if (stress_sighandler(args->name, SIGUSR2, stress_usr2_handler, NULL) < 0)
  460         return EXIT_FAILURE;
  461 
  462     sysfs_path = signum_path;
  463 
  464     ctxt.args = args;
  465     ctxt.writeable = (geteuid() != 0);
  466     ctxt.kmsgfd = open("/dev/kmsg", O_RDONLY | O_NONBLOCK);
  467     (void)stress_kmsg_drain(ctxt.kmsgfd);
  468 
  469     rc = shim_pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE);
  470     if (rc) {
  471         pr_inf("%s: pthread_spin_init failed, errno=%d (%s)\n",
  472             args->name, rc, strerror(rc));
  473         return EXIT_NO_RESOURCE;
  474     }
  475 
  476     (void)memset(ret, 0, sizeof(ret));
  477 
  478     ctxt.badbuf = mmap(NULL, SYS_BUF_SZ, PROT_READ,
  479             MAP_SHARED | MAP_ANONYMOUS, -1, 0);
  480     if (ctxt.badbuf == MAP_FAILED) {
  481         pr_inf("%s: mmap failed: errno=%d (%s)\n",
  482             args->name, errno, strerror(errno));
  483         return EXIT_NO_RESOURCE;
  484     }
  485 
  486     for (i = 0; i < MAX_READ_THREADS; i++) {
  487         ret[i] = pthread_create(&pthreads[i], NULL,
  488                 stress_sys_rw_thread, &ctxt);
  489     }
  490 
  491     do {
  492         stress_sys_dir(&ctxt, "/sys", true, 0);
  493     } while (keep_stressing() && !segv_abort);
  494 
  495     rc = shim_pthread_spin_lock(&lock);
  496     if (rc) {
  497         pr_dbg("%s: failed to lock spin lock for sysfs_path\n", args->name);
  498     } else {
  499         sysfs_path = "";
  500         rc = shim_pthread_spin_unlock(&lock);
  501         (void)rc;
  502     }
  503 
  504     /* Forcefully kill threads */
  505     for (i = 0; i < MAX_READ_THREADS; i++) {
  506         if (ret[i] == 0)
  507             (void)pthread_kill(pthreads[i], SIGHUP);
  508     }
  509 
  510     for (i = 0; i < MAX_READ_THREADS; i++) {
  511         if (ret[i] == 0)
  512             (void)pthread_join(pthreads[i], NULL);
  513     }
  514     if (ctxt.kmsgfd != -1)
  515         (void)close(ctxt.kmsgfd);
  516     (void)munmap(ctxt.badbuf, SYS_BUF_SZ);
  517     (void)shim_pthread_spin_destroy(&lock);
  518 
  519     return EXIT_SUCCESS;
  520 }
  521 
  522 stressor_info_t stress_sysfs_info = {
  523     .stressor = stress_sysfs,
  524     .class = CLASS_OS
  525 };
  526 #else
  527 stressor_info_t stress_sysfs_info = {
  528     .stressor = stress_not_implemented,
  529     .class = CLASS_OS
  530 };
  531 #endif