blob: 5f13d48dda0dba24f02bb000980891ebec5b57e9 [file] [log] [blame]
/******************************************************************************/
/* */
/* Copyright (c) International Business Machines Corp., 2001 */
/* Copyright (c) 2001 Manoj Iyer <manjo@austin.ibm.com> */
/* Copyright (c) 2003 Robbie Williamson <robbiew@us.ibm.com> */
/* Copyright (c) 2004 Paul Larson <plars@linuxtestproject.org> */
/* Copyright (c) 2007 <rsalveti@linux.vnet.ibm.com> */
/* Copyright (c) 2007 Suzuki K P <suzuki@in.ibm.com> */
/* Copyright (c) 2011 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 */
/* */
/******************************************************************************/
/******************************************************************************/
/* Description: Test the LINUX memory manager. The program is aimed at */
/* stressing the memory manager by simultanious map/unmap/read */
/* by light weight processes, the test is scheduled to run for */
/* a mininum of 24 hours. */
/* */
/* Create two light weight processes X and Y. */
/* X - maps, writes and unmap a file in a loop. */
/* Y - read from this mapped region in a loop. */
/* read must be a success between map and unmap of the region. */
/* */
/******************************************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sched.h>
#include <unistd.h>
#include <errno.h>
#include <sys/mman.h>
#include <sched.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <setjmp.h>
#include <pthread.h>
#include <signal.h>
#include <string.h>
#include "test.h"
#define OPT_MISSING(prog, opt) do { \
fprintf(stderr, "%s: option -%c ", prog, opt); \
fprintf(stderr, "requires an argument\n"); \
usage(prog); \
} while (0)
static int verbose_print = 0;
static char *volatile map_address;
static jmp_buf jmpbuf;
static volatile char read_lock = 0;
char *TCID = "mmap1";
int TST_TOTAL = 1;
static void sig_handler(int signal, siginfo_t * info, void *ut)
{
switch (signal) {
case SIGALRM:
tst_resm(TPASS, "Test ended, success");
_exit(TPASS);
case SIGSEGV:
longjmp(jmpbuf, 1);
break;
default:
fprintf(stderr, "Unexpected signal - %d --- exiting\n", signal);
_exit(TBROK);
}
}
/*
* Signal handler that is active, when file is mapped, eg. we do not expect
* SIGSEGV to be delivered.
*/
static void sig_handler_mapped(int signal, siginfo_t * info, void *ut)
{
switch (signal) {
case SIGALRM:
tst_resm(TPASS, "Test ended, success");
_exit(TPASS);
case SIGSEGV:
tst_resm(TINFO, "[%lu] Unexpected page fault at %p",
pthread_self(), info->si_addr);
_exit(TFAIL);
break;
default:
fprintf(stderr, "Unexpected signal - %d --- exiting\n", signal);
_exit(TBROK);
}
}
int mkfile(int size)
{
char template[] = "/tmp/ashfileXXXXXX";
int fd, i;
if ((fd = mkstemp(template)) == -1)
tst_brkm(TBROK | TERRNO, NULL, "mkstemp() failed");
unlink(template);
for (i = 0; i < size; i++)
if (write(fd, "a", 1) == -1)
tst_brkm(TBROK | TERRNO, NULL, "write() failed");
if (write(fd, "\0", 1) == -1)
tst_brkm(TBROK | TERRNO, NULL, "write() failed");
if (fsync(fd) == -1)
tst_brkm(TBROK | TERRNO, NULL, "fsync() failed");
return fd;
}
void *map_write_unmap(void *ptr)
{
struct sigaction sa;
long *args = ptr;
long i;
int j;
tst_resm(TINFO, "[%lu] - map, change contents, unmap files %ld times",
pthread_self(), args[2]);
if (verbose_print)
tst_resm(TINFO, "map_write_unmap() arguments are: "
"fd - arg[0]: %ld; "
"size of file - arg[1]: %ld; "
"num of map/write/unmap - arg[2]: %ld",
args[0], args[1], args[2]);
for (i = 0; i < args[2]; i++) {
map_address = mmap(0, (size_t) args[1], PROT_WRITE | PROT_READ,
MAP_SHARED, (int)args[0], 0);
if (map_address == (void *)-1) {
perror("map_write_unmap(): mmap()");
pthread_exit((void *)1);
}
while (read_lock)
sched_yield();
sigfillset(&sa.sa_mask);
sigdelset(&sa.sa_mask, SIGSEGV);
sa.sa_flags = SA_SIGINFO | SA_NODEFER;
sa.sa_sigaction = sig_handler_mapped;
if (sigaction(SIGSEGV, &sa, NULL)) {
perror("map_write_unmap(): sigaction()");
pthread_exit((void *)1);
}
if (verbose_print)
tst_resm(TINFO, "map address = %p", map_address);
for (j = 0; j < args[1]; j++) {
map_address[j] = 'a';
if (random() % 2)
sched_yield();
}
if (verbose_print)
tst_resm(TINFO,
"[%ld] times done: of total [%ld] iterations, "
"map_write_unmap():memset() content of memory = %s",
i, args[2], (char *)map_address);
sigfillset(&sa.sa_mask);
sigdelset(&sa.sa_mask, SIGSEGV);
sa.sa_flags = SA_SIGINFO | SA_NODEFER;
sa.sa_sigaction = sig_handler;
if (sigaction(SIGSEGV, &sa, NULL)) {
perror("map_write_unmap(): sigaction()");
pthread_exit((void *)1);
}
if (munmap(map_address, (size_t) args[1]) == -1) {
perror("map_write_unmap(): mmap()");
pthread_exit((void *)1);
}
}
pthread_exit(NULL);
}
void *read_mem(void *ptr)
{
long i;
long *args = ptr;
int j;
tst_resm(TINFO, "[%lu] - read contents of memory %p %ld times",
pthread_self(), map_address, args[2]);
if (verbose_print)
tst_resm(TINFO, "read_mem() arguments are: "
"number of reads to be performed - arg[2]: %ld; "
"read from address %p", args[2], map_address);
for (i = 0; i < args[2]; i++) {
if (verbose_print)
tst_resm(TINFO, "read_mem() in while loop %ld times "
"to go %ld times", i, args[2]);
if (setjmp(jmpbuf) == 1) {
read_lock = 0;
if (verbose_print)
tst_resm(TINFO, "page fault occurred due to "
"a read after an unmap");
} else {
if (verbose_print) {
read_lock = 1;
tst_resm(TINFO,
"read_mem(): content of memory: %s",
(char *)map_address);
read_lock = 0;
}
for (j = 0; j < args[1]; j++) {
read_lock = 1;
if (map_address[j] != 'a')
pthread_exit((void *)-1);
read_lock = 0;
if (random() % 2)
sched_yield();
}
}
}
pthread_exit(NULL);
}
static void usage(char *progname)
{
fprintf(stderr, "Usage: %s -d -l -s -v -x\n"
"\t -h help, usage message.\n"
"\t -l number of mmap/write/unmap default: 1000\n"
"\t -s size of the file to be mmapped default: 1024 bytes\n"
"\t -v print more info. default: quiet\n"
"\t -x test execution time default: 24 Hrs\n",
progname);
exit(-1);
}
struct signal_info {
int signum;
char *signame;
};
static struct signal_info sig_info[] = {
{SIGHUP, "SIGHUP"},
{SIGINT, "SIGINT"},
{SIGQUIT, "SIGQUIT"},
{SIGABRT, "SIGABRT"},
{SIGBUS, "SIGBUS"},
{SIGSEGV, "SIGSEGV"},
{SIGALRM, "SIGALRM"},
{SIGUSR1, "SIGUSR1"},
{SIGUSR2, "SIGUSR2"},
{-1, "ENDSIG"}
};
int main(int argc, char **argv)
{
int c, i;
int file_size;
int num_iter;
double exec_time;
int fd;
void *status;
pthread_t thid[2];
long chld_args[3];
extern char *optarg;
struct sigaction sigptr;
int ret;
/* set up the default values */
file_size = 1024;
num_iter = 1000;
exec_time = 24;
while ((c = getopt(argc, argv, "hvl:s:x:")) != -1) {
switch (c) {
case 'h':
usage(argv[0]);
break;
case 'l':
if ((num_iter = atoi(optarg)) == 0)
OPT_MISSING(argv[0], optopt);
else if (num_iter < 0)
printf
("WARNING: bad argument. Using default %d\n",
(num_iter = 1000));
break;
case 's':
if ((file_size = atoi(optarg)) == 0)
OPT_MISSING(argv[0], optopt);
else if (file_size < 0)
printf
("WARNING: bad argument. Using default %d\n",
(file_size = 1024));
break;
case 'v':
verbose_print = 1;
break;
case 'x':
exec_time = atof(optarg);
if (exec_time == 0)
OPT_MISSING(argv[0], optopt);
else if (exec_time < 0)
printf
("WARNING: bad argument. Using default %.0f\n",
(exec_time = 24));
break;
default:
usage(argv[0]);
break;
}
}
if (verbose_print)
tst_resm(TINFO, "Input parameters are: File size: %d; "
"Scheduled to run: %lf hours; "
"Number of mmap/write/read: %d",
file_size, exec_time, num_iter);
alarm(exec_time * 3600);
/* Do not mask SIGSEGV, as we are interested in handling it. */
sigptr.sa_sigaction = sig_handler;
sigfillset(&sigptr.sa_mask);
sigdelset(&sigptr.sa_mask, SIGSEGV);
sigptr.sa_flags = SA_SIGINFO | SA_NODEFER;
for (i = 0; sig_info[i].signum != -1; i++) {
if (sigaction(sig_info[i].signum, &sigptr, NULL) == -1) {
perror("man(): sigaction()");
fprintf(stderr,
"could not set handler for %s, errno = %d\n",
sig_info[i].signame, errno);
exit(-1);
}
}
for (;;) {
if ((fd = mkfile(file_size)) == -1)
tst_brkm(TBROK, NULL,
"main(): mkfile(): Failed to create temp file");
if (verbose_print)
tst_resm(TINFO, "Tmp file created");
chld_args[0] = fd;
chld_args[1] = file_size;
chld_args[2] = num_iter;
if ((ret =
pthread_create(&thid[0], NULL, map_write_unmap,
chld_args)))
tst_brkm(TBROK, NULL, "main(): pthread_create(): %s",
strerror(ret));
tst_resm(TINFO, "created writing thread[%lu]", thid[0]);
if ((ret = pthread_create(&thid[1], NULL, read_mem, chld_args)))
tst_brkm(TBROK, NULL, "main(): pthread_create(): %s",
strerror(ret));
tst_resm(TINFO, "created reading thread[%lu]", thid[1]);
for (i = 0; i < 2; i++) {
if ((ret = pthread_join(thid[i], &status)))
tst_brkm(TBROK, NULL,
"main(): pthread_join(): %s",
strerror(ret));
if (status)
tst_brkm(TFAIL, NULL,
"thread [%lu] - process exited "
"with %ld", thid[i], (long)status);
}
close(fd);
}
exit(0);
}