aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2001-10-18 06:15:32 +0000
committerH. Peter Anvin <hpa@zytor.com>2001-10-18 06:15:32 +0000
commit3adff3ab0f554dc8d08c563f61b93e7c1ca80805 (patch)
tree6021f89e30cd33b74700615161e889135134133b
parent26f21f20a407f7982a9710e95bdc7496a2609edd (diff)
downloadlpsm-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--Makefile5
-rw-r--r--arena.c19
-rw-r--r--lpsm.h1
-rw-r--r--testrecovery.c214
4 files changed, 238 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index 36f6643..405c629 100644
--- a/Makefile
+++ b/Makefile
@@ -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 $@ $<
diff --git a/arena.c b/arena.c
index 7ab018e..4d3f611 100644
--- a/arena.c
+++ b/arena.c
@@ -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);
+}
diff --git a/lpsm.h b/lpsm.h
index 65875d0..d0763e5 100644
--- a/lpsm.h
+++ b/lpsm.h
@@ -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;
+}