diff options
author | H. Peter Anvin <hpa@zytor.com> | 2001-10-18 06:15:32 +0000 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2001-10-18 06:15:32 +0000 |
commit | 3adff3ab0f554dc8d08c563f61b93e7c1ca80805 (patch) | |
tree | 6021f89e30cd33b74700615161e889135134133b | |
parent | 26f21f20a407f7982a9710e95bdc7496a2609edd (diff) | |
download | lpsm-3adff3ab0f554dc8d08c563f61b93e7c1ca80805.tar.gz lpsm-3adff3ab0f554dc8d08c563f61b93e7c1ca80805.tar.xz lpsm-3adff3ab0f554dc8d08c563f61b93e7c1ca80805.zip |
Change the object store to use a file-backed mmap by default.
*** THIS MIGHT HAVE TO BE BACKED OUT IF THE LINUX KERNEL IS TOO DUMB ***
Add recovery test.
-rw-r--r-- | Makefile | 5 | ||||
-rw-r--r-- | arena.c | 19 | ||||
-rw-r--r-- | lpsm.h | 1 | ||||
-rw-r--r-- | testrecovery.c | 214 |
4 files changed, 238 insertions, 1 deletions
@@ -1,4 +1,4 @@ -TEST = teststore test_mmap ftrunctest testbuddy testalloc +TEST = teststore test_mmap ftrunctest testbuddy testalloc testrecovery SONAME = libobjstore.so.0 VERSION = 0.0.1 OBJSTORE = libobjstore.so libobjstore.a @@ -50,6 +50,9 @@ testbuddy: testbuddy.o libobjstore.a testalloc: testalloc.o libobjstore.a $(CC) $(LDFLAGS) -o $@ $< libobjstore.a +testrecovery: testrecovery.o libobjstore.a + $(CC) $(LDFLAGS) -o $@ $< libobjstore.a + test_mmap: test_mmap.o $(CC) $(LDFLAGS) -o $@ $< @@ -655,3 +655,22 @@ int objstore_extend(size_t new_size) while ( fcntl(os->main_fd, F_SETLKW, &lock) == -1 && errno == EINTR ); return -1; } + +/* + * Shut down the object store, free all resources. + * THIS DOES NOT CHECKPOINT - call objstore_checkpoint() first + * if you want that functionality. Calling this function without + * first checkpointing and then calling objstore_init() can be used + * to (very slowly) roll back to the last commit point. + */ +void objstore_shutdown(void) +{ + struct ObjStore *os = objstore_os_struct; + + munmap(os->arena, os->arena_len); + free(os->pageinfo); + sigaction(SIGSEGV, &os->oldact, NULL); + close(os->log_fd); + close(os->main_fd); + free(os); +} @@ -37,6 +37,7 @@ enum objsync_enum { void *objstore_init(const char *, const char *, size_t *); int objstore_checkpoint(double, enum objsync_enum); int objstore_extend(size_t); +void objstore_shutdown(void); /* Arena management routines */ diff --git a/testrecovery.c b/testrecovery.c new file mode 100644 index 0000000..4c14b01 --- /dev/null +++ b/testrecovery.c @@ -0,0 +1,214 @@ +#ident "$Id$" +/* ----------------------------------------------------------------------- * + * + * Copyright 2001 H. Peter Anvin - All Rights Reserved + * + * 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, Inc., 675 Mass Ave, Cambridge MA 02139, + * USA; either version 2 of the License, or (at your option) any later + * version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * testrecovery.c + * + * This test verifies that we can indeed recover from a failure gracefully. + * It is basically the same as the allocation test, except it keeps data- + * structures in persistent memory and periodically kills off the processes. + */ + +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <math.h> +#include <unistd.h> +#include <limits.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/wait.h> +#include "objstore.h" + +#define COUNT 32768 +#define SLOTS 1024 +#define MAXLOG 26 + +void **areas; +int *sizes; + +void verify_no_overlap(int slot) +{ + char *start = areas[slot]; + char *end = start+sizes[slot]; + char *xs, *xe; + int i; + + for ( i = 0 ; i < SLOTS ; i++ ) { + if ( i != slot && areas[i] ) { + xs = areas[i]; + xe = xs + sizes[i]; + + if ( xs < end && xe > start ) { + printf("OVERLAP ALERT: [%p,%p) -> [%p,%p)\n", + start, end, xs, xe); + abort(); + } + } + } +} + +struct misc_info { + unsigned long occupied; + int count; +}; + +int main(int argc, char *argv[]) +{ + int i, slot, newsize; + double rnd; + void **root_ptr; + struct misc_info *misc_ptr; + void *nptr; + pid_t pid; + pid_t master_pid = getpid(); + + /* Do initial setup */ + + setlinebuf(stdout); + setlinebuf(stderr); + + unlink("arena.dat"); + unlink("arena.log"); + + if ( !(root_ptr = objstore_arena_init("arena.dat", "arena.log")) ) { + fprintf(stderr, "%s: objstore_arena_init() failed\n", argv[0]); + return 1; + } + + printf("Arena: initialized, %d root pointers at %p\n", + ROOT_DATA_POINTERS, root_ptr); + + root_ptr[0] = misc_ptr = objstore_malloc(sizeof(struct misc_info)); + root_ptr[1] = areas = objstore_malloc(sizeof(void *) * SLOTS); + root_ptr[2] = sizes = objstore_malloc(sizeof(int) * SLOTS); + + misc_ptr->count = misc_ptr->occupied = 0; + + memset(areas, 0, sizeof(areas)); + memset(sizes, 0, sizeof(sizes)); + + objstore_checkpoint(0.0, OBJSYNC_SYNC); + objstore_shutdown(); + + /* Become our own process group and ignore SIGTERM to make it easier to + "kill everything". Become our own session to keep shell job control + from interfering with stop signals. */ + + setpgrp(); + + while ( 1 ) { + int master_waits = rand() & 1; + + pid = fork(); + if ( pid < 0 ) { + perror("fork"); + return 1; + } else if ( pid > 0 ) { + int status; + + /* Give the child process a reasonable chance to finish log replay, + otherwise we'll never get through the test. On the other hand, + we want to check for failures during log replay too! */ + if ( master_waits ) { + waitpid(pid, &status, WUNTRACED); + if ( !WIFSTOPPED(status) ) { + printf("*** CHILD ERROR, exit code = 0x%x\n", status); + exit(1); + } + kill(pid, SIGCONT); + } + sleep(rand() & 63); + signal(SIGTERM, SIG_IGN); + kill(-master_pid, SIGTERM); + signal(SIGTERM, SIG_DFL); + waitpid(pid, &status, 0); + + if ( WIFEXITED(status) ) + break; /* Child exited, not killed, consider us done */ + } else { + pid = getpid(); + + printf("*** Process starting: %d, master_waits = %d\n", + pid, master_waits); + + root_ptr = objstore_arena_init("arena.dat", "arena.log"); + + misc_ptr = root_ptr[0]; + areas = root_ptr[1]; + sizes = root_ptr[2]; + + printf("*** Recovering at count %d, occupied = %ld,\n" + "*** areas = %p, sizes = %p\n", + misc_ptr->count, misc_ptr->occupied, areas, sizes); + + /* In case the master process decided to be kind and wait for replay */ + if ( master_waits ) + kill(pid, SIGTSTP); /* The master will wait for this and SIGCONT */ + + for ( i = misc_ptr->count+1 ; i <= COUNT ; i++ ) { + misc_ptr->count = i; + slot = rand() % SLOTS; + + printf("Count: %d Slot: %d\n", i, slot); + + if ( areas[slot] ) { + if ( rand() % 1 ) { + objstore_free(areas[slot]); + printf("Free: %d (0x%08x) bytes at %p\n", + sizes[slot], sizes[slot], areas[slot]); + areas[slot] = NULL; + misc_ptr->occupied -= sizes[slot]; + } else { + rnd = (double)rand()/RAND_MAX; + newsize = (int)pow(2.0, MAXLOG*pow(rnd,3.0)); + nptr = objstore_realloc(areas[slot], newsize); + if ( nptr ) { + printf("Realloc: %d bytes at %p -> %d bytes at %p\n", + sizes[slot], areas[slot], newsize, nptr); + misc_ptr->occupied += newsize; + misc_ptr->occupied -= sizes[slot]; + sizes[slot] = newsize; + areas[slot] = nptr; + verify_no_overlap(slot); + } else { + printf("Realloc: %d bytes at %p -> %d bytes FAILED\n", + sizes[slot], areas[slot], newsize); + } + } + } else { + rnd = (double)rand()/RAND_MAX; + sizes[slot] = (int)pow(2.0, MAXLOG*pow(rnd,5.0)); + areas[slot] = objstore_malloc(sizes[slot]); + printf("Alloc: %d (0x%08x) bytes at %p\n", + sizes[slot], sizes[slot], areas[slot]); + misc_ptr->occupied += sizes[slot]; + verify_no_overlap(slot); + } + + if ( (i & 255) == 0 ) { + printf("Arena: checkpointing, count = %d\n", i); + objstore_checkpoint(0.1, OBJSYNC_SKIP); + } + printf("Arena: %ld bytes officially occupied\n", misc_ptr->occupied); + } + /* If the loop finishes, exit... */ + fprintf(stderr, "ready...\n"); + objstore_checkpoint(0.0, OBJSYNC_SYNC); /* Synchronous checkpoint */ + exit(0); + } + } + + return 0; +} |