diff --git a/src/Makefile b/src/Makefile index 680e3866b..9820ffcc8 100644 --- a/src/Makefile +++ b/src/Makefile @@ -166,9 +166,9 @@ REDIS_SERVER_NAME=redis-server REDIS_SENTINEL_NAME=redis-sentinel REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o lolwut.o lolwut5.o acl.o storage.o REDIS_CLI_NAME=redis-cli -REDIS_CLI_OBJ=anet.o adlist.o dict.o redis-cli.o zmalloc.o release.o anet.o ae.o crc64.o siphash.o crc16.o storage.o +REDIS_CLI_OBJ=anet.o adlist.o dict.o redis-cli.o zmalloc.o release.o anet.o ae.o crc64.o siphash.o crc16.o storage-lite.o REDIS_BENCHMARK_NAME=redis-benchmark -REDIS_BENCHMARK_OBJ=ae.o anet.o redis-benchmark.o adlist.o zmalloc.o redis-benchmark.o storage.o +REDIS_BENCHMARK_OBJ=ae.o anet.o redis-benchmark.o adlist.o zmalloc.o redis-benchmark.o storage-lite.o REDIS_CHECK_RDB_NAME=redis-check-rdb REDIS_CHECK_AOF_NAME=redis-check-aof diff --git a/src/config.c b/src/config.c index be1c45235..5f5fd6792 100644 --- a/src/config.c +++ b/src/config.c @@ -804,6 +804,8 @@ void loadServerConfigFromString(char *config) { err = sentinelHandleConfiguration(argv+1,argc-1); if (err) goto loaderr; } + } else if (!strcasecmp(argv[0],"scratch-file-path")) { + storage_init(argv[1]); } else { err = "Bad directive or wrong number of arguments"; goto loaderr; } diff --git a/src/object.c b/src/object.c index ddd50b553..a5da2b142 100644 --- a/src/object.c +++ b/src/object.c @@ -39,7 +39,7 @@ /* ===================== Creation and parsing of objects ==================== */ robj *createObject(int type, void *ptr) { - robj *o = salloc_obj(); //zmalloc(sizeof(*o), MALLOC_SHARED); + robj *o = zmalloc(sizeof(*o), MALLOC_SHARED); o->type = type; o->encoding = OBJ_ENCODING_RAW; o->ptr = ptr; @@ -362,10 +362,7 @@ void decrRefCount(robj *o) { case OBJ_STREAM: freeStreamObject(o); break; default: serverPanic("Unknown object type"); break; } - if (o->type == OBJ_STRING && o->encoding == OBJ_ENCODING_EMBSTR) - zfree(o); - else - sfree_obj(o); + zfree(o); } else { if (o->refcount <= 0) serverPanic("decrRefCount against refcount <= 0"); if (o->refcount != OBJ_SHARED_REFCOUNT) o->refcount--; diff --git a/src/redis-benchmark.c b/src/redis-benchmark.c index 716c492df..5dffd46b6 100644 --- a/src/redis-benchmark.c +++ b/src/redis-benchmark.c @@ -653,7 +653,7 @@ int main(int argc, const char **argv) { client c; - storage_init(); + storage_init(NULL); srandom(time(NULL)); signal(SIGHUP, SIG_IGN); diff --git a/src/redis-cli.c b/src/redis-cli.c index 91deac496..abcfd2b78 100644 --- a/src/redis-cli.c +++ b/src/redis-cli.c @@ -7176,7 +7176,7 @@ static void intrinsicLatencyMode(void) { int main(int argc, char **argv) { int firstarg; - storage_init(); + storage_init(NULL); config.hostip = sdsnew("127.0.0.1"); config.hostport = 6379; config.hostsocket = NULL; diff --git a/src/server.c b/src/server.c index 62b37dfa1..1444100b7 100644 --- a/src/server.c +++ b/src/server.c @@ -4740,7 +4740,7 @@ int main(int argc, char **argv) { struct timeval tv; int j; - storage_init(); + storage_init(NULL); #ifdef REDIS_TEST if (argc == 3 && !strcasecmp(argv[1], "test")) { diff --git a/src/storage-lite.c b/src/storage-lite.c new file mode 100644 index 000000000..ed199ef45 --- /dev/null +++ b/src/storage-lite.c @@ -0,0 +1,41 @@ +#include +#include +#include +#include +#include +#include +#include +#include "storage.h" +#include + +// initialize the memory subsystem. +// NOTE: This may be called twice, first with NULL specifying we should use ram +// later, after the configuration file is loaded with a path to where we should +// place our temporary file. +void storage_init(const char *tmpfilePath) +{ + assert(tmpfilePath == NULL); + (void)tmpfilePath; +} + +void *salloc(size_t cb, enum MALLOC_CLASS class) +{ + (void)class; + return malloc(cb); +} + +void *scalloc(size_t cb, size_t c, enum MALLOC_CLASS class) +{ + (void)class; + return calloc(cb, c); +} + +void sfree(void *pv) +{ + free(pv); +} + +void *srealloc(void *pv, size_t cb) +{ + return realloc(pv, cb); +} diff --git a/src/storage.c b/src/storage.c index 759c99840..d6fcc1b8f 100644 --- a/src/storage.c +++ b/src/storage.c @@ -10,7 +10,7 @@ #include "storage.h" struct memkind *mkdisk = NULL; -static char *PMEM_DIR = "/mnt/btrfs_scratch/"; +static const char *PMEM_DIR = NULL; void handle_prefork(); void handle_postfork_parent(); @@ -85,9 +85,6 @@ void *pool_alloc(struct alloc_pool *ppool) } } -#pragma weak serverLog -void serverLog(int level, const char*fmt, ...){} - void pool_free(struct alloc_pool *ppool, void *pv) { struct object_page *cur = ppool->pobjpageHead; @@ -113,18 +110,62 @@ void pool_free(struct alloc_pool *ppool, void *pv) struct alloc_pool poolobj; struct alloc_pool poolembstrobj; -void storage_init() +int forkFile() { - int errv = memkind_create_pmem(PMEM_DIR, 0, &mkdisk); - if (errv) + int fdT; + memkind_tmpfile(PMEM_DIR, &fdT); + if (ioctl(fdT, FICLONE, memkind_fd(mkdisk)) == -1) { - fprintf(stderr, "Memory pool creation failed: %d\n", errv); - exit(EXIT_FAILURE); + return -1; } - pool_initialize(&poolobj, sizeof(robj)); - pool_initialize(&poolembstrobj, EMBSTR_ROBJ_SIZE); + return fdT; +} - pthread_atfork(handle_prefork, handle_postfork_parent, handle_postfork_child); +// initialize the memory subsystem. +// NOTE: This may be called twice, first with NULL specifying we should use ram +// later, after the configuration file is loaded with a path to where we should +// place our temporary file. +void storage_init(const char *tmpfilePath) +{ + if (tmpfilePath == NULL) + { + serverAssert(mkdisk == NULL); + mkdisk = MEMKIND_DEFAULT; + } + else + { + // First create the file + serverAssert(mkdisk == MEMKIND_DEFAULT); + PMEM_DIR = memkind_malloc(MEMKIND_DEFAULT, strlen(tmpfilePath)); + strcpy((char*)PMEM_DIR, tmpfilePath); + int errv = memkind_create_pmem(PMEM_DIR, 0, &mkdisk); + if (errv == MEMKIND_ERROR_INVALID) + { + serverLog(LOG_CRIT, "Memory pool creation failed: %s", strerror(errno)); + exit(EXIT_FAILURE); + } + else if (errv) + { + char msgbuf[1024]; + memkind_error_message(errv, msgbuf, 1024); + serverLog(LOG_CRIT, "Memory pool creation failed: %s", msgbuf); + exit(EXIT_FAILURE); + } + + // Next test if COW is working + int fdTest = forkFile(); + if (fdTest < 0) + { + serverLog(LOG_ERR, "Scratch file system does not support Copy on Write. To fix this scratch-file-path must point to a path on a filesystem which supports copy on write, such as btrfs."); + exit(EXIT_FAILURE); + } + close(fdTest); + + pool_initialize(&poolobj, sizeof(robj)); + pool_initialize(&poolembstrobj, EMBSTR_ROBJ_SIZE); + + pthread_atfork(handle_prefork, handle_postfork_parent, handle_postfork_child); + } } @@ -184,12 +225,9 @@ void *srealloc(void *pv, size_t cb) int fdNew = -1; void handle_prefork() { - memkind_tmpfile(PMEM_DIR, &fdNew); - if (ioctl(fdNew, FICLONE, memkind_fd(mkdisk)) == -1) - { - perror("failed to fork file"); - exit(EXIT_FAILURE); - } + fdNew = forkFile(); + if (fdNew < 0) + serverLog(LOG_ERR, "Failed to clone scratch file"); } void handle_postfork_parent() diff --git a/src/storage.h b/src/storage.h index 882113cdc..f35bc62e3 100644 --- a/src/storage.h +++ b/src/storage.h @@ -9,12 +9,7 @@ enum MALLOC_CLASS MALLOC_SHARED, }; -void storage_init(void); - -struct redisObject *salloc_obj(); -void sfree_obj(struct redisObject *obj); -struct redisObject *salloc_objembstr(); -void sfree_objembstr(struct redisObject *obj); +void storage_init(const char *tmpfilePath); void *salloc(size_t cb, enum MALLOC_CLASS class); void *scalloc(size_t cb, size_t c, enum MALLOC_CLASS class);