/******************************************************************************
* *
* Copyright (c) International Business Machines Corp., 2001 *
* 2001 Created by Manoj Iyer, IBM Austin TX <manjo@austin.ibm.com> *
* Copyright (C) 2016 Cyril Hrubis <chrubis@suse.cz> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See *
* the GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation, *
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
* *
******************************************************************************/
/*
* Tests the LINUX memory manager. The program is aimed at stressing the memory
* manager by repeaded map/write/unmap of file/memory of random size (maximum
* 1GB) this is done by multiple threads.
*
* Create a file of random size upto 1000 times 4096, map it, change the
* contents of the file and unmap it. This is repeated several times for the
* specified number of hours by a certain number of threads.
*/
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <pthread.h>
#include "tst_safe_pthread.h"
#include "tst_test.h"
static char *str_loops;
static char *str_threads;
static char *map_private;
static char *str_exec_time;
static int loops = 1000;
static int threads = 40;
static float exec_time = 24;
static volatile int sig_caught;
static int threads_running;
static int mkfile(int *size)
{
int fd;
int index = 0;
char buf[4096];
char template[PATH_MAX];
memset(buf, 'a', 4096);
snprintf(template, PATH_MAX, "ashfileXXXXXX");
if ((fd = mkstemp(template)) == -1)
tst_brk(TBROK | TERRNO, "mkstemp()");
unlink(template);
*size = (1 + (int)(1000.0 * rand() / (RAND_MAX + 1.0))) * 4096;
while (index < *size) {
index += sizeof(buf);
SAFE_WRITE(1, fd, buf, sizeof(buf));
}
fsync(fd);
return fd;
}
static void exit_thread(void) __attribute__ ((noreturn));
static void exit_thread(void)
{
tst_atomic_dec(&threads_running);
pthread_exit(NULL);
}
void *map_write_unmap(void *args)
{
int fsize;
int fd;
int i;
void *addr;
long tid = (long)args;
tst_atomic_inc(&threads_running);
for (i = 0; i < loops; i++) {
if (sig_caught)
exit_thread();
if ((fd = mkfile(&fsize)) == -1)
exit_thread();
addr = SAFE_MMAP(NULL, fsize, PROT_WRITE | PROT_READ,
map_private ? MAP_PRIVATE : MAP_SHARED, fd, 0);
memset(addr, 'A', fsize);
tst_res(TINFO, "Thread %4li, addr [%p], size %4ikB, iter %4d",
tid, addr, fsize/1024, i);
usleep(1);
SAFE_MUNMAP(addr, fsize);
SAFE_CLOSE(fd);
}
exit_thread();
}
static void sig_handler(int signal)
{
sig_caught = signal;
}
static void test_mmap(void)
{
long i;
pthread_t thids[threads];
alarm(exec_time * 3600);
while (!sig_caught) {
for (i = 0; i < threads; i++) {
SAFE_PTHREAD_CREATE(&thids[i], NULL,
map_write_unmap, (void*)i);
sched_yield();
}
for (i = 0; i < threads; i++)
SAFE_PTHREAD_JOIN(thids[i], NULL);
}
if (sig_caught == SIGALRM) {
tst_res(TPASS, "Test passed");
} else {
tst_res(TFAIL, "Unexpected signal caught %s",
tst_strsig(sig_caught));
}
}
static void setup(void)
{
if (tst_parse_int(str_loops, &loops, 1, INT_MAX))
tst_brk(TBROK, "Invalid number of loops '%s'", str_loops);
if (tst_parse_int(str_threads, &threads, 1, INT_MAX))
tst_brk(TBROK, "Invalid number of threads '%s'", str_threads);
if (tst_parse_float(str_exec_time, &exec_time, 0.0005, 9000))
tst_brk(TBROK, "Invalid execution time '%s'", str_exec_time);
tst_set_timeout(exec_time * 3600 + 300);
SAFE_SIGNAL(SIGALRM, sig_handler);
SAFE_SIGNAL(SIGBUS, sig_handler);
SAFE_SIGNAL(SIGSEGV, sig_handler);
unsigned int seed = time(NULL) % 100;
srand(seed);
tst_res(TINFO, "Seed %u", seed);
tst_res(TINFO, "Number of loops %i", loops);
tst_res(TINFO, "Number of threads %i", threads);
tst_res(TINFO, "MAP_PRIVATE = %i", map_private ? 1 : 0);
tst_res(TINFO, "Execution time %fH", exec_time);
}
static void cleanup(void)
{
static int flag;
if (tst_atomic_inc(&flag) != 1)
exit_thread();
if (!threads_running)
return;
tst_res(TINFO, "Waiting for %i threads to terminate", threads_running);
sig_caught = 1;
while ((volatile int)threads_running > 1) {
tst_res(TINFO, "Running threads %i",
(volatile int)threads_running);
usleep(500000);
}
}
static struct tst_option options[] = {
{"l:", &str_loops, "-l uint Number of map-write-unmap loops"},
{"n:", &str_threads, "-n uint Number of worker threads"},
{"p", &map_private, "-p Turns on MAP_PRIVATE (default MAP_SHARED)"},
{"x:", &str_exec_time, "-x float Execution time in hours (default 24H)"},
{NULL, NULL, NULL}
};
static struct tst_test test = {
.options = options,
.needs_tmpdir = 1,
.setup = setup,
.cleanup = cleanup,
.test_all = test_mmap,
};