blob: a7029cbb8ff70a126dee16828a76d3b4ee30dcf1 [file] [log] [blame]
/*
* Test program for Linux memory error recovery.
* Requires special injection support.
*
* This is a early primitive version of tinjpage.c,
* but simpler to debug in some cases.
*/
#define _GNU_SOURCE 1
#include <sys/mman.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include <stdlib.h>
#include <setjmp.h>
#include <errno.h>
#include <string.h>
#define MADV_POISON 100
#define err(x) perror(x),exit(1)
int count = 20;
int failure = 0;
int total_cases = 0;
sigjmp_buf recover;
int PS;
void sighandler(int sig, siginfo_t *si, void *arg)
{
printf("signal %d code %d addr %p\n", sig, si->si_code, si->si_addr);
if (--count == 0)
exit(1);
siglongjmp(recover, 1);
}
void testmem(char *msg, char *page, int write)
{
printf("%s page %p\n", msg, page);
total_cases++;
if (sigsetjmp(recover,1) == 0) {
if (madvise(page, PS, MADV_POISON) != 0) {
failure++;
perror("madvise");
}
if (write)
*page = 2;
else
printf("%x\n", *(unsigned char *)page);
}
printf("recovered\n");
}
void expecterr(char *msg, int res)
{
if (res == 0)
printf("no error on %s\n", msg);
else
perror(msg);
}
int tempfd(void)
{
static int tmpcount;
int fd;
char buf[30];
snprintf(buf,30,"/tmp/test%d.XXXXXXXX",tmpcount++);
fd = mkstemp(buf);
if (fd >= 0)
unlink(buf);
return fd;
}
#define RANDOM_FILE "/etc/profile"
int main(void)
{
PS = getpagesize();
char *page;
struct sigaction sa = {
.sa_sigaction = sighandler,
.sa_flags = SA_SIGINFO
};
sigaction(SIGBUS, &sa, NULL);
// sigaction(SIGSEGV, &sa, NULL);
page = mmap(NULL, PS, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_POPULATE, 0, 0);
testmem("dirty", page, 1);
page = mmap(NULL, PS, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_LOCKED, 0, 0);
testmem("mlocked", page, 1);
int fd = open(RANDOM_FILE, O_RDONLY);
if (fd < 0) err("open " RANDOM_FILE);
page = mmap(NULL, PS, PROT_READ, MAP_SHARED|MAP_POPULATE, fd, 0);
if (page == (char *)-1) err("mmap");
close(fd);
testmem("clean file", page, 0);
fd = tempfd();
if (fd < 0) err("open testfile");
char *tmp = malloc(PS);
if (!tmp) err("no enough memory");
memset(tmp, 0xff, PS);
write(fd, tmp, PS);
free(tmp);
page = mmap(NULL, PS, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (page == (char*)-1) err("mmap");
*page = 1;
testmem("file dirty", page, 0);
expecterr("msync expect error", msync(page, PS, MS_SYNC));
expecterr("fsync expect error", fsync(fd));
close(fd);
/* hole case still broken in the kernel -- doesn't report error */
fd = tempfd();
if (fd < 0) err("open testfile");
ftruncate(fd, PS);
page = mmap(NULL, PS, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (page == (char*)-1) err("mmap");
*page = 1;
testmem("hole file dirty", page, 0);
expecterr("hole msync expect error", msync(page, PS, MS_SYNC));
expecterr("hole fsync expect error", fsync(fd));
close(fd);
#if 0
const int NPAGES = 10;
int i;
fd = tempfd();
if (fd < 0) err("open rfp testfile");
tmp = malloc(PS);
if (!tmp) exit(ENOMEM);
for (i = 0; i < NPAGES; i++) {
memset(tmp, i, PS);
write(fd, tmp, PS);
}
free(tmp);
page = mmap(NULL, PS*NPAGES, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (page == (char*)-1) err("mmap");
int k = NPAGES - 1;
for (i = 0; i < NPAGES; i++, k--) {
if (remap_file_pages(page + i*PS, PS, 0, k, 0))
perror("remap_file_pages");
}
*page = 1;
testmem("rfp file dirty", page, 0);
expecterr("rfp msync expect error", msync(page, PS, MS_SYNC));
expecterr("rfp fsync expect error", fsync(fd));
close(fd);
#endif
if (failure > 0) {
printf("FAILURE -- %d of %d cases broken!\n", failure, total_cases);
return 1;
}
printf("SUCCESS\n");
return 0;
}