diff options
author | Naoya Horiguchi <n-horiguchi@ah.jp.nec.com> | 2010-05-20 10:58:42 +0200 |
---|---|---|
committer | Andi Kleen <ak@linux.intel.com> | 2010-05-20 10:58:42 +0200 |
commit | a5c0bbf15e776011af446cfcf5af5d687175cf3b (patch) | |
tree | 795a120f99ef631c9410ca30f6fc0afc2f60d8dc | |
parent | cc456a4fe3e0e3f64df107c025db3501c4d44fc1 (diff) | |
download | mce-test-a5c0bbf15e776011af446cfcf5af5d687175cf3b.tar.gz |
Add hugetlb test for hugetlb mca recovery testing
So far not hooked up to standard "make test" because
the kernel patches are not in yet.
Signed-off-by: Andi Kleen <ak@linux.intel.com>
-rw-r--r-- | tsrc/Makefile | 2 | ||||
-rwxr-xr-x | tsrc/run-huge-test.sh | 168 | ||||
-rw-r--r-- | tsrc/thugetlb.c | 359 |
3 files changed, 528 insertions, 1 deletions
diff --git a/tsrc/Makefile b/tsrc/Makefile index 8276d29..5b10e9a 100644 --- a/tsrc/Makefile +++ b/tsrc/Makefile @@ -5,7 +5,7 @@ CFLAGS += -I ${LSRC}/arch/x86/kernel/cpu/mcheck/ -g -Wall KFLAGS := -I ./kinclude -EXE := tinjpage tsimpleinj tkillpoison tprctl tsoft tsoftinj +EXE := tinjpage tsimpleinj tkillpoison tprctl tsoft tsoftinj thugetlb EXEKERNEL := tring ttable OBJ := $(addsuffix .o,${EXE}) diff --git a/tsrc/run-huge-test.sh b/tsrc/run-huge-test.sh new file mode 100755 index 0000000..5b7380d --- /dev/null +++ b/tsrc/run-huge-test.sh @@ -0,0 +1,168 @@ +#!/bin/bash +# +# Test program for memory error handling for hugepages +# Usage: ./run-huge-test.sh hugetlbfs_directory +# Author: Naoya Horiguchi + +usage() +{ + echo "Usage: ./run-hgue-test.sh hugetlbfs_directory" && exit 1 +} + +htdir=$1 +[ $# -ne 1 ] && usage +[ ! -d $htdir ] && usage + +rm -rf $htdir/test* +echo 1000 > /proc/sys/vm/nr_hugepages + +num=0 + +exec_testcase() { + error=0 + echo "TestCase $@" + hpage_size=$1 + hpage_target=$2 + num=$7 + + if [ "$3" = "head" ] ; then + hpage_target_offset=0 + elif [ "$3" = "tail" ] ; then + hpage_target_offset=1 + else + error=1 + fi + hpage_target=$((hpage_target * 512 + hpage_target_offset)) + + if [ "$4" = "early" ] ; then + process_type="-e" + elif [ "$4" = "late_touch" ] ; then + process_type="" + elif [ "$4" = "late_avoid" ] ; then + process_type="-a" + else + error=1 + fi + + if [ "$5" = "anonymous" ] ; then + file_type="-A" + elif [ "$5" = "file" ] ; then + file_type="-f $num" + elif [ "$5" = "shm" ] ; then + file_type="-S" + else + error=1 + fi + + if [ "$6" = "fork_shared" ] ; then + share_type="-F" + elif [ "$6" = "fork_private_nocow" ] ; then + share_type="-Fp" + elif [ "$6" = "fork_private_cow" ] ; then + share_type="-Fpc" + else + error=1 + fi + + command="./thugetlb -x -m $hpage_size -o $hpage_target $process_type $file_type $share_type $htdir &" + echo $command + eval $command + wait $! + echo "" + + return 0 +} + +num=$((num+1)) +exec_testcase 2 1 "head" "early" "file" "fork_shared" $num +num=$((num+1)) +exec_testcase 2 1 "head" "early" "file" "fork_private_nocow" $num +num=$((num+1)) +exec_testcase 2 1 "head" "early" "file" "fork_private_cow" $num +num=$((num+1)) +exec_testcase 2 1 "head" "early" "shm" "fork_shared" $num +num=$((num+1)) +exec_testcase 2 1 "head" "early" "anonymous" "fork_shared" $num +num=$((num+1)) +exec_testcase 2 1 "head" "early" "anonymous" "fork_private_nocow" $num +num=$((num+1)) +exec_testcase 2 1 "head" "early" "anonymous" "fork_private_cow" $num + +num=$((num+1)) +exec_testcase 2 1 "head" "late_touch" "file" "fork_shared" $num +num=$((num+1)) +exec_testcase 2 1 "head" "late_touch" "file" "fork_private_nocow" $num +num=$((num+1)) +exec_testcase 2 1 "head" "late_touch" "file" "fork_private_cow" $num +num=$((num+1)) +exec_testcase 2 1 "head" "late_touch" "shm" "fork_shared" $num +num=$((num+1)) +exec_testcase 2 1 "head" "late_touch" "anonymous" "fork_shared" $num +num=$((num+1)) +exec_testcase 2 1 "head" "late_touch" "anonymous" "fork_private_nocow" $num +num=$((num+1)) +exec_testcase 2 1 "head" "late_touch" "anonymous" "fork_private_cow" $num + +num=$((num+1)) +exec_testcase 2 1 "head" "late_avoid" "file" "fork_shared" $num +num=$((num+1)) +exec_testcase 2 1 "head" "late_avoid" "file" "fork_private_nocow" $num +num=$((num+1)) +exec_testcase 2 1 "head" "late_avoid" "file" "fork_private_cow" $num +num=$((num+1)) +exec_testcase 2 1 "head" "late_avoid" "shm" "fork_shared" $num +num=$((num+1)) +exec_testcase 2 1 "head" "late_avoid" "anonymous" "fork_shared" $num +num=$((num+1)) +exec_testcase 2 1 "head" "late_avoid" "anonymous" "fork_private_nocow" $num +num=$((num+1)) +exec_testcase 2 1 "head" "late_avoid" "anonymous" "fork_private_cow" $num + +num=$((num+1)) +exec_testcase 2 1 "tail" "early" "file" "fork_shared" $num +num=$((num+1)) +exec_testcase 2 1 "tail" "early" "file" "fork_private_nocow" $num +num=$((num+1)) +exec_testcase 2 1 "tail" "early" "file" "fork_private_cow" $num +num=$((num+1)) +exec_testcase 2 1 "tail" "early" "shm" "fork_shared" $num +num=$((num+1)) +exec_testcase 2 1 "tail" "early" "anonymous" "fork_shared" $num +num=$((num+1)) +exec_testcase 2 1 "tail" "early" "anonymous" "fork_private_nocow" $num +num=$((num+1)) +exec_testcase 2 1 "tail" "early" "anonymous" "fork_private_cow" $num + +num=$((num+1)) +exec_testcase 2 1 "tail" "late_touch" "file" "fork_shared" $num +num=$((num+1)) +exec_testcase 2 1 "tail" "late_touch" "file" "fork_private_nocow" $num +num=$((num+1)) +exec_testcase 2 1 "tail" "late_touch" "file" "fork_private_cow" $num +num=$((num+1)) +exec_testcase 2 1 "tail" "late_touch" "shm" "fork_shared" $num +num=$((num+1)) +exec_testcase 2 1 "tail" "late_touch" "anonymous" "fork_shared" $num +num=$((num+1)) +exec_testcase 2 1 "tail" "late_touch" "anonymous" "fork_private_nocow" $num +num=$((num+1)) +exec_testcase 2 1 "tail" "late_touch" "anonymous" "fork_private_cow" $num + +num=$((num+1)) +exec_testcase 2 1 "tail" "late_avoid" "file" "fork_shared" $num +num=$((num+1)) +exec_testcase 2 1 "tail" "late_avoid" "file" "fork_private_nocow" $num +num=$((num+1)) +exec_testcase 2 1 "tail" "late_avoid" "file" "fork_private_cow" $num +num=$((num+1)) +exec_testcase 2 1 "tail" "late_avoid" "shm" "fork_shared" $num +num=$((num+1)) +exec_testcase 2 1 "tail" "late_avoid" "anonymous" "fork_shared" $num +num=$((num+1)) +exec_testcase 2 1 "tail" "late_avoid" "anonymous" "fork_private_nocow" $num +num=$((num+1)) +exec_testcase 2 1 "tail" "late_avoid" "anonymous" "fork_private_cow" $num + +# free IPC semaphores used by thugetlb.c +ipcs -s|grep $USER|cut -f2 -d' '|xargs ipcrm sem + diff --git a/tsrc/thugetlb.c b/tsrc/thugetlb.c new file mode 100644 index 0000000..35e0b7e --- /dev/null +++ b/tsrc/thugetlb.c @@ -0,0 +1,359 @@ +/* + * Test program for memory error handling for hugepages + * Author: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com> + */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <signal.h> +#include <unistd.h> +#include <getopt.h> +#include <sys/mman.h> +#include <sys/ipc.h> +#include <sys/shm.h> +#include <sys/sem.h> +#include <sys/types.h> +#include <sys/prctl.h> + +#define FILE_BASE "test" + +#define HPAGE_SIZE (2UL*1024*1024) +#define BUF_SIZE 256 +#define PROTECTION (PROT_READ | PROT_WRITE) + +#ifndef SHM_HUGETLB +#define SHM_HUGETLB 04000 +#endif + +/* Control early_kill/late_kill */ +#define PR_MCE_KILL 33 +#define PR_MCE_KILL_CLEAR 0 +#define PR_MCE_KILL_SET 1 +#define PR_MCE_KILL_LATE 0 +#define PR_MCE_KILL_EARLY 1 +#define PR_MCE_KILL_DEFAULT 2 +#define PR_MCE_KILL_GET 34 + +int PS; /* Page size */ +int file_size; /* Memory allocation size (hugepage unit) */ +/* Error injection position (page offset from the first hugepage head) */ +int corrupt_page; +char filename[BUF_SIZE] = "/test"; +char filepath[BUF_SIZE]; + +#define DEB printf("DEBUG [%d:%s:%d]\n", getpid(), __FILE__, __LINE__); + +static void usage(void) +{ + printf( +"./thugetlb [-m memory] [-o offset] [-f file] [-xeSAaFpch] hugetlbfs_directory\n" +" -m|--memory size(hugepage unit) Size of hugetlbfs file\n" +" -o|--offset offset(page unit) Position of error injection\n" +" -x|--inject Error injection switch\n" +" -e|--early-kill Set PR_MCE_KILL_EARLY\n" +" -S|--shm Use shmem with SHM_HUGETLB\n" +" -A|--anonymous Use MAP_ANONYMOUS\n" +" -a|--avoid-touch Avoid touching error page\n" +" -F|--fork\n" +" -p|--private\n" +" -c|--cow\n" +" -f|--filename string\n" +" -h|--help\n" +"\n" + ); +} + +/* + * semaphore get/put wrapper + */ +int get_semaphore(int sem_id, struct sembuf *sembuffer) +{ + sembuffer->sem_num = 0; + sembuffer->sem_op = -1; + sembuffer->sem_flg = SEM_UNDO; + return semop(sem_id, sembuffer, 1); +} + +int put_semaphore(int sem_id, struct sembuf *sembuffer) +{ + sembuffer->sem_num = 0; + sembuffer->sem_op = 1; + sembuffer->sem_flg = SEM_UNDO; + return semop(sem_id, sembuffer, 1); +} + +static int avoid_hpage(void *addr, int flag, char *avoid) +{ + return flag == 1 && addr == avoid; +} + +static void write_bytes(char *addr, int flag, char *avoid) +{ + int i, j; + for (i = 0; i < file_size; i++) { + if (avoid_hpage(addr + i * HPAGE_SIZE, flag, avoid)) + continue; + for (j = 0; j < HPAGE_SIZE; j++) { + *(addr + i * HPAGE_SIZE + j) = (char)('a' + + ((i + j) % 26)); + } + } +} + +static void read_bytes(char *addr, int flag, char *avoid) +{ + int i, j; + + for (i = 0; i < file_size; i++) { + if (avoid_hpage(addr + i * HPAGE_SIZE, flag, avoid)) + continue; + for (j = 0; j < HPAGE_SIZE; j++) { + if (*(addr + i * HPAGE_SIZE + j) != (char)('a' + + ((i + j) % 26))) { + printf("Mismatch at %lu\n", i + j); + break; + } + } + } +} + +static struct option opts[] = { + { "memory" , 1, NULL, 'm' }, + { "offset" , 1, NULL, 'o' }, + { "inject" , 0, NULL, 'x' }, + { "early_kill" , 0, NULL, 'e' }, + { "shm" , 0, NULL, 'S' }, + { "anonymous" , 0, NULL, 'A' }, + { "avoid-touch" , 0, NULL, 'a' }, + { "fork" , 0, NULL, 'F' }, + { "private" , 0, NULL, 'p' }, + { "cow" , 0, NULL, 'c' }, + { "filename" , 1, NULL, 'f' }, + { "help" , 0, NULL, 'h' }, + { NULL , 0, NULL, 0 } +}; + +int main(int argc, char *argv[]) +{ + void *addr; + int i; + int ret; + int fd = 0; + int shmid; + int semid; + int semaphore; + int inject = 0; + int early_kill = 0; + int avoid_touch = 0; + int anonflag = 0; + int shmflag = 0; + int shmkey = 0; + int forkflag = 0; + int privateflag = 0; + int cowflag = 0; + char c; + pid_t pid = 0; + void *expected_addr = NULL; + struct sembuf sembuffer; + + PS = getpagesize(); + file_size = 1; + corrupt_page = -1; + + if (argc == 1) { + usage(); + exit(EXIT_FAILURE); + } + + while ((c = getopt_long(argc, argv, + "m:o:xeSAaFpcf:h", opts, NULL)) != -1) { + switch (c) { + case 'm': + file_size = strtol(optarg, NULL, 10); + break; + case 'o': + corrupt_page = strtol(optarg, NULL, 10); + break; + case 'x': + inject = 1; + break; + case 'e': + early_kill = 1; + break; + case 'S': + shmflag = 1; + break; + case 'A': + anonflag = 1; + break; + case 'a': + avoid_touch = 1; + break; + case 'F': + forkflag = 1; + break; + case 'p': + privateflag = 1; + break; + case 'c': + cowflag = 1; + break; + case 'f': + strcat(filename, optarg); + shmkey = strtol(optarg, NULL, 10); + break; + case 'h': + usage(); + exit(EXIT_SUCCESS); + default: + usage(); + exit(EXIT_FAILURE); + } + } + + if (inject && corrupt_page * PS > file_size * HPAGE_SIZE) + err("Target page is out of range.\n"); + + if (avoid_touch && corrupt_page == -1) + err("Avoid which page?\n"); + + /* Construct file name */ + if (access(argv[argc - 1], F_OK) == -1) { + usage(); + exit(EXIT_FAILURE); + } else { + strcpy(filepath, argv[argc - 1]); + strcat(filepath, filename); + } + + if (shmflag) { + if ((shmid = shmget(shmkey, file_size * HPAGE_SIZE, + SHM_HUGETLB | IPC_CREAT | SHM_R | SHM_W)) < 0) + err("shmget"); + addr = shmat(shmid, (void *)0x0UL, 0); + if (addr == (char *)-1) { + perror("Shared memory attach failure"); + shmctl(shmid, IPC_RMID, NULL); + exit(2); + } + } else if (anonflag) { + int mapflag = MAP_ANONYMOUS | 0x40000; /* MAP_HUGETLB */ + if (privateflag) + mapflag |= MAP_PRIVATE; + else + mapflag |= MAP_SHARED; + if ((addr = mmap(0, file_size * HPAGE_SIZE, + PROTECTION, mapflag, -1, 0)) == MAP_FAILED) + err("mmap"); + } else { + int mapflag = MAP_SHARED; + if (privateflag) + mapflag = MAP_PRIVATE; + if ((fd = open(filepath, O_CREAT | O_RDWR, 0777)) < 0) + err("Open failed"); + if ((addr = mmap(0, file_size * HPAGE_SIZE, + PROTECTION, mapflag, fd, 0)) == MAP_FAILED) { + unlink(filepath); + err("mmap"); + } + } + + if (corrupt_page != -1) + expected_addr = (void *)(addr + corrupt_page / 512 * HPAGE_SIZE); + + if (forkflag) { + semid = semget(IPC_PRIVATE, 1, 0666|IPC_CREAT); + if (semid == -1) { + perror("semget"); + goto cleanout; + } + semaphore = semctl(semid, 0, SETVAL, 1); + if (semaphore == -1) { + perror("semctl"); + goto cleanout; + } + if (get_semaphore(semid, &sembuffer)) { + perror("get_semaphore"); + goto cleanout; + } + } + + write_bytes(addr, 0, 0); + read_bytes(addr, 0, 0); + + if (early_kill) + prctl(PR_MCE_KILL, PR_MCE_KILL_SET, PR_MCE_KILL_EARLY, + NULL, NULL); + + /* + * Intended order: + * 1. Child COWs + * 2. Parent madvise()s + * 3. Child exit()s + */ + if (forkflag) { + pid = fork(); + if (!pid) { + /* Semaphore is already held */ + if (cowflag) { + write_bytes(addr, 0, expected_addr); + read_bytes(addr, 0, expected_addr); + } + if (put_semaphore(semid, &sembuffer)) + err("put_semaphore"); + usleep(1000); + /* Wait for madvise() to be done */ + if (get_semaphore(semid, &sembuffer)) + err("put_semaphore"); + if (put_semaphore(semid, &sembuffer)) + err("put_semaphore"); + return 0; + } + } + + /* Wait for COW */ + if (forkflag && get_semaphore(semid, &sembuffer)) { + perror("get_semaphore"); + goto cleanout; + } + + if (inject && corrupt_page != -1) { + ret = madvise(addr + corrupt_page * PS, PS, 100); + if (ret) { + printf("madivise return %d :", ret); + perror("madvise"); + goto cleanout; + } + } + + if (forkflag && put_semaphore(semid, &sembuffer)) { + perror("put_semaphore"); + goto cleanout; + } + + write_bytes(addr, avoid_touch, expected_addr); + read_bytes(addr, avoid_touch, expected_addr); + + if (forkflag) + if (wait(&i) == -1) + err("wait"); +cleanout: + if (shmflag) { + if (shmdt((const void *)addr) != 0) { + err("Detach failure"); + shmctl(shmid, IPC_RMID, NULL); + exit(EXIT_FAILURE); + } + shmctl(shmid, IPC_RMID, NULL); + } else { + if (munmap(addr, file_size * HPAGE_SIZE)) + err("munmap"); + if (close(fd)) + err("close"); + if (!anonflag && unlink(filepath)) + err("unlink"); + } + + return 0; +} |