|
Packit |
4e8bc4 |
#include "memcached.h"
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
#include "restart.h"
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
#include <stdio.h>
|
|
Packit |
4e8bc4 |
#include <stdlib.h>
|
|
Packit |
4e8bc4 |
#include <sys/mman.h>
|
|
Packit |
4e8bc4 |
#include <sys/types.h>
|
|
Packit |
4e8bc4 |
#include <sys/stat.h>
|
|
Packit |
4e8bc4 |
#include <fcntl.h>
|
|
Packit |
4e8bc4 |
#include <string.h>
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
typedef struct _restart_data_cb restart_data_cb;
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
struct _restart_data_cb {
|
|
Packit |
4e8bc4 |
void *data; // user supplied opaque data.
|
|
Packit |
4e8bc4 |
struct _restart_data_cb *next; // callbacks are ordered stack
|
|
Packit |
4e8bc4 |
restart_check_cb ccb;
|
|
Packit |
4e8bc4 |
restart_save_cb scb;
|
|
Packit |
4e8bc4 |
char tag[RESTART_TAG_MAXLEN];
|
|
Packit |
4e8bc4 |
};
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
// TODO: struct to hand back to caller.
|
|
Packit |
4e8bc4 |
static int mmap_fd = 0;
|
|
Packit |
4e8bc4 |
static void *mmap_base = NULL;
|
|
Packit |
4e8bc4 |
static size_t slabmem_limit = 0;
|
|
Packit |
4e8bc4 |
char *memory_file = NULL;
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
static restart_data_cb *cb_stack = NULL;
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
// Allows submodules and engines to have independent check and save metadata
|
|
Packit |
4e8bc4 |
// routines for the restart code.
|
|
Packit |
4e8bc4 |
void restart_register(const char *tag, restart_check_cb ccb, restart_save_cb scb, void *data) {
|
|
Packit |
4e8bc4 |
restart_data_cb *cb = calloc(1, sizeof(restart_data_cb));
|
|
Packit |
4e8bc4 |
if (cb == NULL) {
|
|
Packit |
4e8bc4 |
fprintf(stderr, "[restart] failed to allocate callback register\n");
|
|
Packit |
4e8bc4 |
abort();
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
// Handle first time call initialization inline so we don't need separate
|
|
Packit |
4e8bc4 |
// API call.
|
|
Packit |
4e8bc4 |
if (cb_stack == NULL) {
|
|
Packit |
4e8bc4 |
cb_stack = cb;
|
|
Packit |
4e8bc4 |
} else {
|
|
Packit |
4e8bc4 |
// Ensure we fire the callbacks in registration order.
|
|
Packit |
4e8bc4 |
// Someday I'll get a queue.h overhaul.
|
|
Packit |
4e8bc4 |
restart_data_cb *finder = cb_stack;
|
|
Packit |
4e8bc4 |
while (finder->next != NULL) {
|
|
Packit |
4e8bc4 |
finder = finder->next;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
finder->next = cb;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
safe_strcpy(cb->tag, tag, RESTART_TAG_MAXLEN);
|
|
Packit |
4e8bc4 |
cb->data = data;
|
|
Packit |
4e8bc4 |
cb->ccb = *ccb;
|
|
Packit |
4e8bc4 |
cb->scb = *scb;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
typedef struct {
|
|
Packit |
4e8bc4 |
FILE *f;
|
|
Packit |
4e8bc4 |
restart_data_cb *cb;
|
|
Packit |
4e8bc4 |
char *line;
|
|
Packit |
4e8bc4 |
bool done;
|
|
Packit |
4e8bc4 |
} restart_cb_ctx;
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
// TODO: error string from cb?
|
|
Packit |
4e8bc4 |
// - look for final line with checksum
|
|
Packit |
4e8bc4 |
// - checksum entire file (up until final line)
|
|
Packit |
4e8bc4 |
// - seek to start
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
static int restart_check(const char *file) {
|
|
Packit |
4e8bc4 |
// metadata is kept in a separate file.
|
|
Packit |
4e8bc4 |
size_t flen = strlen(file);
|
|
Packit |
4e8bc4 |
const char *ext = ".meta";
|
|
Packit |
4e8bc4 |
char *metafile = calloc(1, flen + strlen(ext) + 1);
|
|
Packit |
4e8bc4 |
if (metafile == NULL) {
|
|
Packit |
4e8bc4 |
// probably in a really bad position if we hit here, so don't start.
|
|
Packit |
4e8bc4 |
fprintf(stderr, "[restart] failed to allocate memory for restart check\n");
|
|
Packit |
4e8bc4 |
abort();
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
memcpy(metafile, file, flen);
|
|
Packit |
4e8bc4 |
memcpy(metafile+flen, ext, strlen(ext));
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
FILE *f = fopen(metafile, "r");
|
|
Packit |
4e8bc4 |
if (f == NULL) {
|
|
Packit |
4e8bc4 |
fprintf(stderr, "[restart] no metadata save file, starting with a clean cache\n");
|
|
Packit |
4e8bc4 |
free(metafile);
|
|
Packit |
4e8bc4 |
return -1;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
restart_cb_ctx ctx;
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
ctx.f = f;
|
|
Packit |
4e8bc4 |
ctx.cb = NULL;
|
|
Packit |
4e8bc4 |
ctx.line = NULL;
|
|
Packit |
4e8bc4 |
ctx.done = false;
|
|
Packit |
4e8bc4 |
if (restart_get_kv(&ctx, NULL, NULL) != RESTART_DONE) {
|
|
Packit |
4e8bc4 |
// First line must be a tag, so read it in and set up the proper
|
|
Packit |
4e8bc4 |
// callback here.
|
|
Packit |
4e8bc4 |
fprintf(stderr, "[restart] corrupt metadata file\n");
|
|
Packit |
4e8bc4 |
// TODO: this should probably just return -1 and skip the reuse.
|
|
Packit |
4e8bc4 |
abort();
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
if (ctx.cb == NULL) {
|
|
Packit |
4e8bc4 |
fprintf(stderr, "[restart] Failed to read a tag from metadata file\n");
|
|
Packit |
4e8bc4 |
abort();
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
// loop call the callback, check result code.
|
|
Packit |
4e8bc4 |
bool failed = false;
|
|
Packit |
4e8bc4 |
while (!ctx.done) {
|
|
Packit |
4e8bc4 |
restart_data_cb *cb = ctx.cb;
|
|
Packit |
4e8bc4 |
if (cb->ccb(cb->tag, &ctx, cb->data) != 0) {
|
|
Packit |
4e8bc4 |
failed = true;
|
|
Packit |
4e8bc4 |
break;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
if (ctx.line)
|
|
Packit |
4e8bc4 |
free(ctx.line);
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
fclose(f);
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
unlink(metafile);
|
|
Packit |
4e8bc4 |
free(metafile);
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
if (failed) {
|
|
Packit |
4e8bc4 |
fprintf(stderr, "[restart] failed to valiate metadata, starting with a clean cache\n");
|
|
Packit |
4e8bc4 |
return -1;
|
|
Packit |
4e8bc4 |
} else {
|
|
Packit |
4e8bc4 |
return 0;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
// This function advances the file read while being called directly from the
|
|
Packit |
4e8bc4 |
// callback.
|
|
Packit |
4e8bc4 |
// The control inversion here (callback calling in which might change the next
|
|
Packit |
4e8bc4 |
// callback) allows the callbacks to set up proper loops or sequences for
|
|
Packit |
4e8bc4 |
// reading data back, avoiding an event model.
|
|
Packit |
4e8bc4 |
enum restart_get_kv_ret restart_get_kv(void *ctx, char **key, char **val) {
|
|
Packit |
4e8bc4 |
char *line = NULL;
|
|
Packit |
4e8bc4 |
size_t len = 0;
|
|
Packit |
4e8bc4 |
restart_data_cb *cb = NULL;
|
|
Packit |
4e8bc4 |
restart_cb_ctx *c = (restart_cb_ctx *) ctx;
|
|
Packit |
4e8bc4 |
// free previous line.
|
|
Packit |
4e8bc4 |
// we could just pass it into getline, but it can randomly realloc so we'd
|
|
Packit |
4e8bc4 |
// have to re-assign it into the structure anyway.
|
|
Packit |
4e8bc4 |
if (c->line != NULL) {
|
|
Packit |
4e8bc4 |
free(c->line);
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
if (getline(&line, &len, c->f) != -1) {
|
|
Packit |
4e8bc4 |
// First char is an indicator:
|
|
Packit |
4e8bc4 |
// T for TAG, changing the callback we use.
|
|
Packit |
4e8bc4 |
// K for key/value, to ship to the active callback.
|
|
Packit |
4e8bc4 |
char *p = line;
|
|
Packit |
4e8bc4 |
while (*p != '\n') {
|
|
Packit |
4e8bc4 |
p++;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
*p = '\0';
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
if (line[0] == 'T') {
|
|
Packit |
4e8bc4 |
cb = cb_stack;
|
|
Packit |
4e8bc4 |
while (cb != NULL) {
|
|
Packit |
4e8bc4 |
// NOTE: len is allocated size, not line len. need to chomp \n
|
|
Packit |
4e8bc4 |
if (strcmp(cb->tag, line+1) == 0) {
|
|
Packit |
4e8bc4 |
break;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
cb = cb->next;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
if (cb == NULL) {
|
|
Packit |
4e8bc4 |
fprintf(stderr, "[restart] internal handler for metadata tag not found: %s:\n", line+1);
|
|
Packit |
4e8bc4 |
return RESTART_NOTAG;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
c->cb = cb;
|
|
Packit |
4e8bc4 |
} else if (line[0] == 'K') {
|
|
Packit |
4e8bc4 |
char *p = line+1; // start just ahead of the token.
|
|
Packit |
4e8bc4 |
// tokenize the string and return the pointers?
|
|
Packit |
4e8bc4 |
if (key != NULL) {
|
|
Packit |
4e8bc4 |
*key = p;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
// turn key into a normal NULL terminated string.
|
|
Packit |
4e8bc4 |
while (*p != ' ' && (p - line < len)) {
|
|
Packit |
4e8bc4 |
p++;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
*p = '\0';
|
|
Packit |
4e8bc4 |
p++;
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
// value _should_ run until where the newline was, which is \0 now
|
|
Packit |
4e8bc4 |
if (val != NULL) {
|
|
Packit |
4e8bc4 |
*val = p;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
c->line = line;
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
return RESTART_OK;
|
|
Packit |
4e8bc4 |
} else {
|
|
Packit |
4e8bc4 |
// FIXME: proper error chain.
|
|
Packit |
4e8bc4 |
fprintf(stderr, "[restart] invalid metadata line:\n\n%s\n", line);
|
|
Packit |
4e8bc4 |
return RESTART_BADLINE;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
} else {
|
|
Packit |
4e8bc4 |
// EOF or error in read.
|
|
Packit |
4e8bc4 |
c->done = true;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
return RESTART_DONE;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
// TODO:
|
|
Packit |
4e8bc4 |
// - rolling checksum along with the writes.
|
|
Packit |
4e8bc4 |
// - write final line + checksum + byte count or w/e.
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
static int restart_save(const char *file) {
|
|
Packit |
4e8bc4 |
// metadata is kept in a separate file.
|
|
Packit |
4e8bc4 |
// FIXME: function.
|
|
Packit |
4e8bc4 |
size_t flen = strlen(file);
|
|
Packit |
4e8bc4 |
const char *ext = ".meta";
|
|
Packit |
4e8bc4 |
size_t extlen = strlen(ext);
|
|
Packit |
4e8bc4 |
char *metafile = calloc(1, flen + extlen + 1);
|
|
Packit |
4e8bc4 |
if (metafile == NULL) {
|
|
Packit |
4e8bc4 |
fprintf(stderr, "[restart] failed to allocate memory during metadata save\n");
|
|
Packit |
4e8bc4 |
return -1;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
memcpy(metafile, file, flen);
|
|
Packit |
4e8bc4 |
memcpy(metafile+flen, ext, extlen);
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
// restrictive permissions for the metadata file.
|
|
Packit |
4e8bc4 |
// TODO: also for the mmap file eh? :P
|
|
Packit |
4e8bc4 |
mode_t oldmask = umask(~(S_IRUSR | S_IWUSR));
|
|
Packit |
4e8bc4 |
FILE *f = fopen(metafile, "w");
|
|
Packit |
4e8bc4 |
umask(oldmask);
|
|
Packit |
4e8bc4 |
if (f == NULL) {
|
|
Packit |
4e8bc4 |
// FIXME: correct error handling.
|
|
Packit |
4e8bc4 |
free(metafile);
|
|
Packit |
4e8bc4 |
perror("failed to write metadata file");
|
|
Packit |
4e8bc4 |
return -1;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
restart_data_cb *cb = cb_stack;
|
|
Packit |
4e8bc4 |
restart_cb_ctx ctx;
|
|
Packit |
4e8bc4 |
ctx.f = f;
|
|
Packit |
4e8bc4 |
while (cb != NULL) {
|
|
Packit |
4e8bc4 |
// Plugins/engines in the metadata file are separated by tag lines.
|
|
Packit |
4e8bc4 |
fprintf(f, "T%s\n", cb->tag);
|
|
Packit |
4e8bc4 |
if (cb->scb(cb->tag, &ctx, cb->data) != 0) {
|
|
Packit |
4e8bc4 |
fclose(f);
|
|
Packit |
4e8bc4 |
free(metafile);
|
|
Packit |
4e8bc4 |
return -1;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
cb = cb->next;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
fclose(f);
|
|
Packit |
4e8bc4 |
free(metafile);
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
return 0;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
// Keys and values must not contain spaces or newlines.
|
|
Packit |
4e8bc4 |
// Could offer an interface that uriencodes values for the caller, however
|
|
Packit |
4e8bc4 |
// nothing currently would use it, so add when necessary.
|
|
Packit |
4e8bc4 |
#define SET_VAL_MAX 4096
|
|
Packit |
4e8bc4 |
void restart_set_kv(void *ctx, const char *key, const char *fmt, ...) {
|
|
Packit |
4e8bc4 |
va_list ap;
|
|
Packit |
4e8bc4 |
restart_cb_ctx *c = (restart_cb_ctx *) ctx;
|
|
Packit |
4e8bc4 |
char valbuf[SET_VAL_MAX];
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
va_start(ap, fmt);
|
|
Packit |
4e8bc4 |
int vlen = vsnprintf(valbuf, SET_VAL_MAX-1, fmt, ap);
|
|
Packit |
4e8bc4 |
va_end(ap);
|
|
Packit |
4e8bc4 |
// This is heavy handed. We need to protect against corrupt data as much
|
|
Packit |
4e8bc4 |
// as possible. The buffer is large and these values are currently small,
|
|
Packit |
4e8bc4 |
// it will take a significant mistake to land here.
|
|
Packit |
4e8bc4 |
if (vlen >= SET_VAL_MAX) {
|
|
Packit |
4e8bc4 |
fprintf(stderr, "[restart] fatal error while saving metadata state, value too long for: %s %s",
|
|
Packit |
4e8bc4 |
key, valbuf);
|
|
Packit |
4e8bc4 |
abort();
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
fprintf(c->f, "K%s %s\n", key, valbuf);
|
|
Packit |
4e8bc4 |
// TODO: update crc32c
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
static long _find_pagesize(void) {
|
|
Packit |
4e8bc4 |
#if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
|
|
Packit |
4e8bc4 |
return sysconf(_SC_PAGESIZE);
|
|
Packit |
4e8bc4 |
#else
|
|
Packit |
4e8bc4 |
// A good guess.
|
|
Packit |
4e8bc4 |
return 4096;
|
|
Packit |
4e8bc4 |
#endif
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
bool restart_mmap_open(const size_t limit, const char *file, void **mem_base) {
|
|
Packit |
4e8bc4 |
bool reuse_mmap = true;
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
long pagesize = _find_pagesize();
|
|
Packit |
4e8bc4 |
memory_file = strdup(file);
|
|
Packit |
4e8bc4 |
mmap_fd = open(file, O_RDWR|O_CREAT, S_IRWXU);
|
|
Packit |
4e8bc4 |
if (mmap_fd == -1) {
|
|
Packit |
4e8bc4 |
perror("failed to open file for mmap");
|
|
Packit |
4e8bc4 |
abort();
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
if (ftruncate(mmap_fd, limit) != 0) {
|
|
Packit |
4e8bc4 |
perror("ftruncate failed");
|
|
Packit |
4e8bc4 |
abort();
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
/* Allocate everything in a big chunk with malloc */
|
|
Packit |
4e8bc4 |
if (limit % pagesize) {
|
|
Packit |
4e8bc4 |
// This is a sanity check; shouldn't ever be possible since we
|
|
Packit |
4e8bc4 |
// increase memory by whole megabytes.
|
|
Packit |
4e8bc4 |
fprintf(stderr, "[restart] memory limit not divisible evenly by pagesize (please report bug)\n");
|
|
Packit |
4e8bc4 |
abort();
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
mmap_base = mmap(NULL, limit, PROT_READ|PROT_WRITE, MAP_SHARED, mmap_fd, 0);
|
|
Packit |
4e8bc4 |
if (mmap_base == MAP_FAILED) {
|
|
Packit |
4e8bc4 |
perror("failed to mmap, aborting");
|
|
Packit |
4e8bc4 |
abort();
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
// Set the limit before calling check_mmap, so we can find the meta page..
|
|
Packit |
4e8bc4 |
slabmem_limit = limit;
|
|
Packit |
4e8bc4 |
if (restart_check(file) != 0) {
|
|
Packit |
4e8bc4 |
reuse_mmap = false;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
*mem_base = mmap_base;
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
return reuse_mmap;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
/* Gracefully stop/close the shared memory segment */
|
|
Packit |
4e8bc4 |
void restart_mmap_close(void) {
|
|
Packit |
4e8bc4 |
msync(mmap_base, slabmem_limit, MS_SYNC);
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
if (restart_save(memory_file) != 0) {
|
|
Packit |
4e8bc4 |
fprintf(stderr, "[restart] failed to save metadata");
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
if (munmap(mmap_base, slabmem_limit) != 0) {
|
|
Packit |
4e8bc4 |
perror("[restart] failed to munmap shared memory");
|
|
Packit |
4e8bc4 |
} else if (close(mmap_fd) != 0) {
|
|
Packit |
4e8bc4 |
perror("[restart] failed to close shared memory fd");
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
free(memory_file);
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
// given memory base, quickly walk memory and do pointer fixup.
|
|
Packit |
4e8bc4 |
// do this once on startup to avoid having to do pointer fixup on every
|
|
Packit |
4e8bc4 |
// reference from hash table or LRU.
|
|
Packit |
4e8bc4 |
unsigned int restart_fixup(void *orig_addr) {
|
|
Packit |
4e8bc4 |
struct timeval tv;
|
|
Packit |
4e8bc4 |
uint64_t checked = 0;
|
|
Packit |
4e8bc4 |
const unsigned int page_size = settings.slab_page_size;
|
|
Packit |
4e8bc4 |
unsigned int page_remain = page_size;
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
gettimeofday(&tv, NULL);
|
|
Packit |
4e8bc4 |
if (settings.verbose > 0) {
|
|
Packit |
4e8bc4 |
fprintf(stderr, "[restart] original memory base: [%p] new base: [%p]\n", orig_addr, mmap_base);
|
|
Packit |
4e8bc4 |
fprintf(stderr, "[restart] recovery start [%d.%d]\n", (int)tv.tv_sec, (int)tv.tv_usec);
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
// since chunks don't align with pages, we have to also track page size.
|
|
Packit |
4e8bc4 |
while (checked < slabmem_limit) {
|
|
Packit |
4e8bc4 |
//fprintf(stderr, "checked: %lu\n", checked);
|
|
Packit |
4e8bc4 |
item *it = (item *)((char *)mmap_base + checked);
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
int size = slabs_fixup((char *)mmap_base + checked,
|
|
Packit |
4e8bc4 |
checked % settings.slab_page_size);
|
|
Packit |
4e8bc4 |
//fprintf(stderr, "id: %d, size: %d\n", it->slabs_clsid, size);
|
|
Packit |
4e8bc4 |
// slabber gobbled an entire page, skip and move on.
|
|
Packit |
4e8bc4 |
if (size == -1) {
|
|
Packit |
4e8bc4 |
assert(page_remain % page_size == 0);
|
|
Packit |
4e8bc4 |
assert(page_remain == page_size);
|
|
Packit |
4e8bc4 |
checked += page_remain;
|
|
Packit |
4e8bc4 |
page_remain = page_size;
|
|
Packit |
4e8bc4 |
continue;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
if (it->it_flags & ITEM_LINKED) {
|
|
Packit |
4e8bc4 |
// fixup next/prev links while on LRU.
|
|
Packit |
4e8bc4 |
if (it->next) {
|
|
Packit |
4e8bc4 |
it->next = (item *)((mc_ptr_t)it->next - (mc_ptr_t)orig_addr);
|
|
Packit |
4e8bc4 |
it->next = (item *)((mc_ptr_t)it->next + (mc_ptr_t)mmap_base);
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
if (it->prev) {
|
|
Packit |
4e8bc4 |
it->prev = (item *)((mc_ptr_t)it->prev - (mc_ptr_t)orig_addr);
|
|
Packit |
4e8bc4 |
it->prev = (item *)((mc_ptr_t)it->prev + (mc_ptr_t)mmap_base);
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
//fprintf(stderr, "item was linked\n");
|
|
Packit |
4e8bc4 |
do_item_link_fixup(it);
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
if (it->it_flags & (ITEM_CHUNKED|ITEM_CHUNK)) {
|
|
Packit |
4e8bc4 |
item_chunk *ch;
|
|
Packit |
4e8bc4 |
if (it->it_flags & ITEM_CHUNKED) {
|
|
Packit |
4e8bc4 |
ch = (item_chunk *) ITEM_schunk(it);
|
|
Packit |
4e8bc4 |
// Sigh. Chunked items are a hack; the clsid is the clsid of
|
|
Packit |
4e8bc4 |
// the full object (always the largest slab class) rather than
|
|
Packit |
4e8bc4 |
// the actual chunk.
|
|
Packit |
4e8bc4 |
// I bet this is fixable :(
|
|
Packit |
4e8bc4 |
size = slabs_size(ch->orig_clsid);
|
|
Packit |
4e8bc4 |
//fprintf(stderr, "fixing chunked item header [%d]\n", size);
|
|
Packit |
4e8bc4 |
} else {
|
|
Packit |
4e8bc4 |
//fprintf(stderr, "fixing item chunk [%d]\n", size);
|
|
Packit |
4e8bc4 |
ch = (item_chunk *) it;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
if (ch->next) {
|
|
Packit |
4e8bc4 |
ch->next = (item_chunk *)((mc_ptr_t)ch->next - (mc_ptr_t)orig_addr);
|
|
Packit |
4e8bc4 |
ch->next = (item_chunk *)((mc_ptr_t)ch->next + (mc_ptr_t)mmap_base);
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
if (ch->prev) {
|
|
Packit |
4e8bc4 |
ch->prev = (item_chunk *)((mc_ptr_t)ch->prev - (mc_ptr_t)orig_addr);
|
|
Packit |
4e8bc4 |
ch->prev = (item_chunk *)((mc_ptr_t)ch->prev + (mc_ptr_t)mmap_base);
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
if (ch->head) {
|
|
Packit |
4e8bc4 |
ch->head = (item *)((mc_ptr_t)ch->head - (mc_ptr_t)orig_addr);
|
|
Packit |
4e8bc4 |
ch->head = (item *)((mc_ptr_t)ch->head + (mc_ptr_t)mmap_base);
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
// next chunk
|
|
Packit |
4e8bc4 |
checked += size;
|
|
Packit |
4e8bc4 |
page_remain -= size;
|
|
Packit |
4e8bc4 |
if (size > page_remain) {
|
|
Packit |
4e8bc4 |
//fprintf(stderr, "doot %d\n", page_remain);
|
|
Packit |
4e8bc4 |
checked += page_remain;
|
|
Packit |
4e8bc4 |
page_remain = settings.slab_page_size;
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
//assert(checked != 3145728);
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
if (settings.verbose > 0) {
|
|
Packit |
4e8bc4 |
gettimeofday(&tv, NULL);
|
|
Packit |
4e8bc4 |
fprintf(stderr, "[restart] recovery end [%d.%d]\n", (int)tv.tv_sec, (int)tv.tv_usec);
|
|
Packit |
4e8bc4 |
}
|
|
Packit |
4e8bc4 |
|
|
Packit |
4e8bc4 |
return 0;
|
|
Packit |
4e8bc4 |
}
|