/* * stress_sigrestore() * restore a handler */ int stress_sigrestore( const char *name, const int signum, struct sigaction *orig_action) { if (sigaction(signum, orig_action, NULL) < 0) { pr_fail(stderr, "%s: sigaction %s restore: errno=%d (%s)\n", name, stress_strsignal(signum), errno, strerror(errno)); return -1; } return 0; }
/* * stress_sighandler() * set signal handler in generic way */ int stress_sighandler( const char *name, const int signum, void (*handler)(int), struct sigaction *orig_action) { struct sigaction new_action; memset(&new_action, 0, sizeof new_action); new_action.sa_handler = handler; sigemptyset(&new_action.sa_mask); new_action.sa_flags = 0; if (sigaction(signum, &new_action, orig_action) < 0) { pr_fail(stderr, "%s: sigaction %s: errno=%d (%s)\n", name, stress_strsignal(signum), errno, strerror(errno)); return -1; } return 0; }
/* * stress_userfaultfd() * stress userfaultfd */ static int stress_userfaultfd(const args_t *args) { pid_t pid; int rc = EXIT_FAILURE; size_t userfaultfd_bytes = DEFAULT_MMAP_BYTES; if (!get_setting("userfaultfd-bytes", &userfaultfd_bytes)) { if (g_opt_flags & OPT_FLAGS_MAXIMIZE) userfaultfd_bytes = MAX_MMAP_BYTES; if (g_opt_flags & OPT_FLAGS_MINIMIZE) userfaultfd_bytes = MIN_MMAP_BYTES; } userfaultfd_bytes /= args->num_instances; if (userfaultfd_bytes < MIN_MMAP_BYTES) userfaultfd_bytes = MIN_MMAP_BYTES; if (userfaultfd_bytes < args->page_size) userfaultfd_bytes = args->page_size; pid = fork(); if (pid < 0) { if (errno == EAGAIN) return EXIT_NO_RESOURCE; pr_err("%s: fork failed: errno=%d: (%s)\n", args->name, errno, strerror(errno)); } else if (pid > 0) { /* Parent */ int status, ret; (void)setpgid(pid, g_pgrp); ret = shim_waitpid(pid, &status, 0); if (ret < 0) { if (errno != EINTR) pr_dbg("%s: waitpid(): errno=%d (%s)\n", args->name, errno, strerror(errno)); (void)kill(pid, SIGTERM); (void)kill(pid, SIGKILL); (void)shim_waitpid(pid, &status, 0); } else if (WIFSIGNALED(status)) { pr_dbg("%s: child died: %s (instance %d)\n", args->name, stress_strsignal(WTERMSIG(status)), args->instance); /* If we got killed by OOM killer, report this */ if (WTERMSIG(status) == SIGKILL) { log_system_mem_info(); pr_dbg("%s: assuming killed by OOM " "killer, aborting " "(instance %d)\n", args->name, args->instance); return EXIT_NO_RESOURCE; } return EXIT_FAILURE; } rc = WEXITSTATUS(status); } else if (pid == 0) { /* Child */ (void)setpgid(0, g_pgrp); stress_parent_died_alarm(); _exit(stress_userfaultfd_oomable(args, userfaultfd_bytes)); } return rc; }
/* * stress_memthrash() * stress by creating pthreads */ static int stress_memthrash(const args_t *args) { const stress_memthrash_method_info_t *memthrash_method = &memthrash_methods[0]; const uint32_t total_cpus = stress_get_processors_configured(); const uint32_t max_threads = stress_memthrash_max(args->num_instances, total_cpus); pthread_t pthreads[max_threads]; int ret[max_threads]; pthread_args_t pargs; memthrash_func_t func; pid_t pid; (void)get_setting("memthrash-method", &memthrash_method); func = memthrash_method->func; pr_dbg("%s: using method '%s'\n", args->name, memthrash_method->name); if (args->instance == 0) { pr_inf("%s: starting %" PRIu32 " thread%s on each of the %" PRIu32 " stressors on a %" PRIu32 " CPU system\n", args->name, max_threads, plural(max_threads), args->num_instances, total_cpus); if (max_threads * args->num_instances > total_cpus) { pr_inf("%s: this is not an optimal choice of stressors, " "try %" PRIu32 " instead\n", args->name, stress_memthash_optimal(args->num_instances, total_cpus)); } } pargs.args = args; pargs.data = func; (void)memset(pthreads, 0, sizeof(pthreads)); (void)memset(ret, 0, sizeof(ret)); (void)sigfillset(&set); again: if (!g_keep_stressing_flag) return EXIT_SUCCESS; pid = fork(); if (pid < 0) { if ((errno == EAGAIN) || (errno == ENOMEM)) goto again; pr_err("%s: fork failed: errno=%d: (%s)\n", args->name, errno, strerror(errno)); } else if (pid > 0) { int status, waitret; /* Parent, wait for child */ (void)setpgid(pid, g_pgrp); waitret = shim_waitpid(pid, &status, 0); if (waitret < 0) { if (errno != EINTR) pr_dbg("%s: waitpid(): errno=%d (%s)\n", args->name, errno, strerror(errno)); (void)kill(pid, SIGTERM); (void)kill(pid, SIGKILL); (void)shim_waitpid(pid, &status, 0); } else if (WIFSIGNALED(status)) { pr_dbg("%s: child died: %s (instance %d)\n", args->name, stress_strsignal(WTERMSIG(status)), args->instance); /* If we got killed by OOM killer, re-start */ if (WTERMSIG(status) == SIGKILL) { log_system_mem_info(); pr_dbg("%s: assuming killed by OOM killer, " "restarting again (instance %d)\n", args->name, args->instance); goto again; } } } else if (pid == 0) { uint32_t i; /* Make sure this is killable by OOM killer */ set_oom_adjustment(args->name, true); int flags = MAP_PRIVATE | MAP_ANONYMOUS; #if defined(MAP_POPULATE) flags |= MAP_POPULATE; #endif mmap_retry: mem = mmap(NULL, MEM_SIZE, PROT_READ | PROT_WRITE, flags, -1, 0); if (mem == MAP_FAILED) { #if defined(MAP_POPULATE) flags &= ~MAP_POPULATE; /* Less aggressive, more OOMable */ #endif if (!g_keep_stressing_flag) { pr_dbg("%s: mmap failed: %d %s\n", args->name, errno, strerror(errno)); return EXIT_NO_RESOURCE; } (void)shim_usleep(100000); if (!g_keep_stressing_flag) goto reap_mem; goto mmap_retry; } for (i = 0; i < max_threads; i++) { ret[i] = pthread_create(&pthreads[i], NULL, stress_memthrash_func, (void *)&pargs); if (ret[i]) { /* Just give up and go to next thread */ if (ret[i] == EAGAIN) continue; /* Something really unexpected */ pr_fail_errno("pthread create", ret[i]); goto reap; } if (!g_keep_stressing_flag) goto reap; } /* Wait for SIGALRM or SIGINT/SIGHUP etc */ (void)pause(); reap: thread_terminate = true; for (i = 0; i < max_threads; i++) { if (!ret[i]) { ret[i] = pthread_join(pthreads[i], NULL); if (ret[i]) pr_fail_errno("pthread join", ret[i]); } } reap_mem: (void)munmap(mem, MEM_SIZE); } return EXIT_SUCCESS; }
/* * stress_mlock() * stress mlock with pages being locked/unlocked */ int stress_mlock( uint64_t *const counter, const uint32_t instance, const uint64_t max_ops, const char *name) { const size_t page_size = stress_get_pagesize(); pid_t pid; size_t max = sysconf(_SC_MAPPED_FILES); max = max > MLOCK_MAX ? MLOCK_MAX : max; again: pid = fork(); if (pid < 0) { if (opt_do_run && (errno == EAGAIN)) goto again; pr_err(stderr, "%s: fork failed: errno=%d: (%s)\n", name, errno, strerror(errno)); } else if (pid > 0) { int status, ret; setpgid(pid, pgrp); stress_parent_died_alarm(); /* Parent, wait for child */ ret = waitpid(pid, &status, 0); if (ret < 0) { if (errno != EINTR) pr_dbg(stderr, "%s: waitpid(): errno=%d (%s)\n", name, errno, strerror(errno)); (void)kill(pid, SIGTERM); (void)kill(pid, SIGKILL); (void)waitpid(pid, &status, 0); } else if (WIFSIGNALED(status)) { pr_dbg(stderr, "%s: child died: %s (instance %d)\n", name, stress_strsignal(WTERMSIG(status)), instance); /* If we got killed by OOM killer, re-start */ if (WTERMSIG(status) == SIGKILL) { pr_dbg(stderr, "%s: assuming killed by OOM " "killer, restarting again " "(instance %d)\n", name, instance); goto again; } } } else if (pid == 0) { uint8_t *mappings[max]; size_t i, n; setpgid(0, pgrp); /* Make sure this is killable by OOM killer */ set_oom_adjustment(name, true); do { for (n = 0; opt_do_run && (n < max); n++) { int ret; if (!opt_do_run || (max_ops && *counter >= max_ops)) break; mappings[n] = mmap(NULL, page_size * 3, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); if (mappings[n] == MAP_FAILED) break; ret = mlock_shim(mappings[n] + page_size, page_size); if (ret < 0) { if (errno == EAGAIN) continue; if (errno == ENOMEM) break; pr_fail_err(name, "mlock"); break; } else { /* * Mappings are always page aligned so * we can use the bottom bit to * indicate if the page has been * mlocked or not */ mappings[n] = (uint8_t *) ((ptrdiff_t)mappings[n] | 1); (*counter)++; } } for (i = 0; i < n; i++) { ptrdiff_t addr = (ptrdiff_t)mappings[i]; ptrdiff_t mlocked = addr & 1; addr ^= mlocked; if (mlocked) (void)munlock((uint8_t *)addr + page_size, page_size); munmap((void *)addr, page_size * 3); } #if !defined(__gnu_hurd__) (void)mlockall(MCL_CURRENT); (void)mlockall(MCL_FUTURE); #if defined(MCL_ONFAULT) (void)mlockall(MCL_ONFAULT); #endif #endif for (n = 0; opt_do_run && (n < max); n++) { if (!opt_do_run || (max_ops && *counter >= max_ops)) break; mappings[n] = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); if (mappings[n] == MAP_FAILED) break; } #if !defined(__gnu_hurd__) (void)munlockall(); #endif for (i = 0; i < n; i++) munmap(mappings[i], page_size); } while (opt_do_run && (!max_ops || *counter < max_ops)); } return EXIT_SUCCESS; }
/* * stress_bigheap() * stress heap allocation */ int stress_bigheap( uint64_t *const counter, const uint32_t instance, const uint64_t max_ops, const char *name) { void *ptr = NULL, *last_ptr = NULL; uint8_t *last_ptr_end = NULL; size_t size = 0; const size_t stride = stress_get_pagesize(); pid_t pid; uint32_t restarts = 0, nomems = 0; const size_t page_size = stress_get_pagesize(); if (!set_bigheap_growth) { if (opt_flags & OPT_FLAGS_MAXIMIZE) opt_bigheap_growth = MAX_BIGHEAP_GROWTH; if (opt_flags & OPT_FLAGS_MINIMIZE) opt_bigheap_growth = MIN_BIGHEAP_GROWTH; } again: if (!opt_do_run) return EXIT_SUCCESS; pid = fork(); if (pid < 0) { if (errno == EAGAIN) goto again; pr_err(stderr, "%s: fork failed: errno=%d: (%s)\n", name, errno, strerror(errno)); } else if (pid > 0) { int status, ret; setpgid(pid, pgrp); /* Parent, wait for child */ ret = waitpid(pid, &status, 0); if (ret < 0) { if (errno != EINTR) pr_dbg(stderr, "%s: waitpid(): errno=%d (%s)\n", name, errno, strerror(errno)); (void)kill(pid, SIGTERM); (void)kill(pid, SIGKILL); (void)waitpid(pid, &status, 0); } else if (WIFSIGNALED(status)) { pr_dbg(stderr, "%s: child died: %s (instance %d)\n", name, stress_strsignal(WTERMSIG(status)), instance); /* If we got killed by OOM killer, re-start */ if (WTERMSIG(status) == SIGKILL) { pr_dbg(stderr, "%s: assuming killed by OOM " "killer, restarting again " "(instance %d)\n", name, instance); restarts++; goto again; } } } else if (pid == 0) { setpgid(0, pgrp); stress_parent_died_alarm(); /* Make sure this is killable by OOM killer */ set_oom_adjustment(name, true); do { void *old_ptr = ptr; size += (size_t)opt_bigheap_growth; /* * With many instances running it is wise to * double check before the next realloc as * sometimes process start up is delayed for * some time and we should bail out before * exerting any more memory pressure */ if (!opt_do_run) goto abort; ptr = realloc(old_ptr, size); if (ptr == NULL) { pr_dbg(stderr, "%s: out of memory at %" PRIu64 " MB (instance %d)\n", name, (uint64_t)(4096ULL * size) >> 20, instance); free(old_ptr); size = 0; nomems++; } else { size_t i, n; uint8_t *u8ptr, *tmp; if (last_ptr == ptr) { tmp = u8ptr = last_ptr_end; n = (size_t)opt_bigheap_growth; } else { tmp = u8ptr = ptr; n = size; } if (page_size > 0) { size_t sz = page_size - 1; uintptr_t pg_ptr = ((uintptr_t)ptr + sz) & ~sz; size_t len = size - (pg_ptr - (uintptr_t)ptr); (void)mincore_touch_pages((void *)pg_ptr, len); } for (i = 0; i < n; i+= stride, u8ptr += stride) { if (!opt_do_run) goto abort; *u8ptr = (uint8_t)i; } if (opt_flags & OPT_FLAGS_VERIFY) { for (i = 0; i < n; i+= stride, tmp += stride) { if (!opt_do_run) goto abort; if (*tmp != (uint8_t)i) pr_fail(stderr, "%s: byte at location %p was 0x%" PRIx8 " instead of 0x%" PRIx8 "\n", name, u8ptr, *tmp, (uint8_t)i); } } last_ptr = ptr; last_ptr_end = u8ptr; } (*counter)++; } while (opt_do_run && (!max_ops || *counter < max_ops));
/* * stress_mremap() * stress mmap */ static int stress_mremap(const args_t *args) { const size_t page_size = args->page_size; size_t sz, new_sz; int rc = EXIT_SUCCESS, flags = MAP_PRIVATE | MAP_ANONYMOUS; pid_t pid; uint32_t ooms = 0, segvs = 0, buserrs = 0; size_t mremap_bytes = DEFAULT_MREMAP_BYTES; #if defined(MAP_POPULATE) flags |= MAP_POPULATE; #endif if (!get_setting("mremap-bytes", &mremap_bytes)) { if (g_opt_flags & OPT_FLAGS_MAXIMIZE) mremap_bytes = MAX_MREMAP_BYTES; if (g_opt_flags & OPT_FLAGS_MINIMIZE) mremap_bytes = MIN_MREMAP_BYTES; } mremap_bytes /= args->num_instances; if (mremap_bytes < MIN_MREMAP_BYTES) mremap_bytes = MIN_MREMAP_BYTES; if (mremap_bytes < page_size) mremap_bytes= page_size; new_sz = sz = mremap_bytes & ~(page_size - 1); /* Make sure this is killable by OOM killer */ set_oom_adjustment(args->name, true); again: if (!g_keep_stressing_flag) return EXIT_SUCCESS; pid = fork(); if (pid < 0) { if ((errno == EAGAIN) || (errno == ENOMEM)) goto again; pr_err("%s: fork failed: errno=%d: (%s)\n", args->name, errno, strerror(errno)); } else if (pid > 0) { int status, ret; (void)setpgid(pid, g_pgrp); /* Parent, wait for child */ ret = shim_waitpid(pid, &status, 0); if (ret < 0) { if (errno != EINTR) pr_dbg("%s: waitpid(): errno=%d (%s)\n", args->name, errno, strerror(errno)); (void)kill(pid, SIGTERM); (void)kill(pid, SIGKILL); (void)shim_waitpid(pid, &status, 0); } else if (WIFSIGNALED(status)) { /* If we got killed by sigbus, re-start */ if (WTERMSIG(status) == SIGBUS) { /* Happens frequently, so be silent */ buserrs++; goto again; } pr_dbg("%s: child died: %s (instance %d)\n", args->name, stress_strsignal(WTERMSIG(status)), args->instance); /* If we got killed by OOM killer, re-start */ if (WTERMSIG(status) == SIGKILL) { if (g_opt_flags & OPT_FLAGS_OOMABLE) { log_system_mem_info(); pr_dbg("%s: assuming killed by OOM " "killer, bailing out " "(instance %d)\n", args->name, args->instance); _exit(0); } else { log_system_mem_info(); pr_dbg("%s: assuming killed by OOM " "killer, restarting again " "(instance %d)\n", args->name, args->instance); ooms++; } goto again; } /* If we got killed by sigsegv, re-start */ if (WTERMSIG(status) == SIGSEGV) { pr_dbg("%s: killed by SIGSEGV, " "restarting again " "(instance %d)\n", args->name, args->instance); segvs++; goto again; } } else { rc = WEXITSTATUS(status); } } else if (pid == 0) { (void)setpgid(0, g_pgrp); stress_parent_died_alarm(); /* Make sure this is killable by OOM killer */ set_oom_adjustment(args->name, true); rc = stress_mremap_child(args, sz, new_sz, page_size, mremap_bytes, &flags); _exit(rc); } if (ooms + segvs + buserrs > 0) pr_dbg("%s: OOM restarts: %" PRIu32 ", SEGV restarts: %" PRIu32 ", SIGBUS signals: %" PRIu32 "\n", args->name, ooms, segvs, buserrs); return rc; }
/* * stress_brk() * stress brk and sbrk */ int stress_brk( uint64_t *const counter, const uint32_t instance, const uint64_t max_ops, const char *name) { pid_t pid; uint32_t restarts = 0, nomems = 0; const size_t page_size = stress_get_pagesize(); again: if (!opt_do_run) return EXIT_SUCCESS; pid = fork(); if (pid < 0) { if (errno == EAGAIN) goto again; pr_err(stderr, "%s: fork failed: errno=%d: (%s)\n", name, errno, strerror(errno)); } else if (pid > 0) { int status, ret; setpgid(pid, pgrp); /* Parent, wait for child */ ret = waitpid(pid, &status, 0); if (ret < 0) { if (errno != EINTR) pr_dbg(stderr, "%s: waitpid(): errno=%d (%s)\n", name, errno, strerror(errno)); (void)kill(pid, SIGTERM); (void)kill(pid, SIGKILL); (void)waitpid(pid, &status, 0); } else if (WIFSIGNALED(status)) { pr_dbg(stderr, "%s: child died: %s (instance %d)\n", name, stress_strsignal(WTERMSIG(status)), instance); /* If we got killed by OOM killer, re-start */ if (WTERMSIG(status) == SIGKILL) { pr_dbg(stderr, "%s: assuming killed by OOM " "killer, restarting again " "(instance %d)\n", name, instance); restarts++; goto again; } } } else if (pid == 0) { uint8_t *start_ptr; bool touch = !(opt_flags & OPT_FLAGS_BRK_NOTOUCH); setpgid(0, pgrp); /* Make sure this is killable by OOM killer */ set_oom_adjustment(name, true); start_ptr = sbrk(0); if (start_ptr == (void *) -1) { pr_err(stderr, "%s: sbrk(0) failed: errno=%d (%s)\n", name, errno, strerror(errno)); exit(EXIT_FAILURE); } do { uint8_t *ptr = sbrk((intptr_t)page_size); if (ptr == (void *)-1) { if (errno == ENOMEM) { nomems++; if (brk(start_ptr) < 0) { pr_err(stderr, "%s: brk(%p) failed: errno=%d (%s)\n", name, start_ptr, errno, strerror(errno)); exit(EXIT_FAILURE); } } else { pr_err(stderr, "%s: sbrk(%d) failed: errno=%d (%s)\n", name, (int)page_size, errno, strerror(errno)); exit(EXIT_FAILURE); } } else { /* Touch page, force it to be resident */ if (touch) *(ptr - 1) = 0; } (*counter)++; } while (opt_do_run && (!max_ops || *counter < max_ops)); } if (restarts + nomems > 0) pr_dbg(stderr, "%s: OOM restarts: %" PRIu32 ", out of memory restarts: %" PRIu32 ".\n", name, restarts, nomems); return EXIT_SUCCESS; }