From 08c8be07b12f2148eab90db099f2b2ef89737d53 Mon Sep 17 00:00:00 2001 From: Sears Russell Date: Thu, 25 Aug 2011 21:29:51 +0000 Subject: [PATCH] add support for fallocate(); enable it by default if it is available at build time. --- CMakeLists.txt | 1 + config.h.cmake | 1 + src/stasis/bufferManager.c | 21 +++++++++++++++++++ src/stasis/bufferManager/bufferHash.c | 7 +++++++ .../bufferManager/concurrentBufferManager.c | 5 +++++ .../legacy/legacyBufferManager.c | 1 + src/stasis/bufferManager/pageArray.c | 1 + src/stasis/flags.c | 11 ++++++++++ src/stasis/io/file.c | 11 ++++++++++ src/stasis/io/pfile.c | 12 ++++++++++- src/stasis/operations/regions.c | 20 ++++++++++-------- src/stasis/pageHandle.c | 7 +++++++ stasis/bufferManager.h | 3 ++- stasis/constants.h | 4 ++++ stasis/flags.h | 9 ++++++++ stasis/io/handle.h | 1 + stasis/pageHandle.h | 3 +++ 17 files changed, 107 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c211a29..0ea22d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,6 +64,7 @@ endif(NOT HAVE_FUSE) INCLUDE(CheckFunctionExists) INCLUDE(CheckCSourceCompiles) CHECK_FUNCTION_EXISTS(sync_file_range HAVE_SYNC_FILE_RANGE) +CHECK_FUNCTION_EXISTS(posix_fallocate HAVE_POSIX_FALLOCATE) CHECK_FUNCTION_EXISTS(fdatasync HAVE_FDATASYNC) CHECK_FUNCTION_EXISTS(tdestroy HAVE_TDESTROY) diff --git a/config.h.cmake b/config.h.cmake index 0531c25..2360349 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -1,5 +1,6 @@ #define __USE_GNU #define _GNU_SOURCE +#cmakedefine HAVE_POSIX_FALLOCATE #cmakedefine HAVE_FDATASYNC #cmakedefine HAVE_SYNC_FILE_RANGE #cmakedefine HAVE_O_DIRECT diff --git a/src/stasis/bufferManager.c b/src/stasis/bufferManager.c index 0d80c26..f6a4a20 100644 --- a/src/stasis/bufferManager.c +++ b/src/stasis/bufferManager.c @@ -202,6 +202,27 @@ void prefetchPages(pageid_t pageid, pageid_t count) { if(bm->prefetchPages != NULL) { bm->prefetchPages(bm, pageid, count); } } +int preallocatePages(pageid_t pageid, pageid_t count) { + stasis_buffer_manager_t * bm = stasis_runtime_buffer_manager(); + // This is just a performance hint; and is an optional method. + if(stasis_buffer_manager_preallocate_mode == STASIS_BUFFER_MANAGER_PREALLOCATE_LEGACY) { + // Legacy option (ext3): pre-fill the stasis buffer manager with dirty zeros. + for(pageid_t i = 0; i < count; i++) { + Page * p = loadUninitializedPage(-1, pageid+i); + stasis_dirty_page_table_set_dirty(stasis_runtime_dirty_page_table(), p); + releasePage(p); + } + } else if(stasis_buffer_manager_preallocate_mode == STASIS_BUFFER_MANAGER_PREALLOCATE_DEFAULT) { + if(bm->preallocatePages != NULL) { return bm->preallocatePages(bm, pageid, count); } + } else if(stasis_buffer_manager_preallocate_mode == STASIS_BUFFER_MANAGER_PREALLOCATE_DISABLED) { + } else { + fprintf(stderr, "Configuration error. Unknown preallocation mode: %d\n", + stasis_buffer_manager_preallocate_mode); + abort(); + } + return 0; +} + Page * getCachedPage(int xid, pageid_t pageid) { stasis_buffer_manager_t * bm = stasis_runtime_buffer_manager(); return bm->getCachedPageImpl(bm, xid, pageid); diff --git a/src/stasis/bufferManager/bufferHash.c b/src/stasis/bufferManager/bufferHash.c index d71059e..d6ba10c 100644 --- a/src/stasis/bufferManager/bufferHash.c +++ b/src/stasis/bufferManager/bufferHash.c @@ -370,6 +370,12 @@ void bhPrefetchPagesImpl(stasis_buffer_manager_t *bm, pageid_t pageid, pageid_t } +static int bhPreallocatePages(stasis_buffer_manager_t * bm, pageid_t start, pageid_t count) { + stasis_buffer_hash_t * bh = bm->impl; + return bh->page_handle->preallocate_range(bh->page_handle, start, count); +} + + static void bhReleasePage(stasis_buffer_manager_t * bm, Page * p) { DEBUG("releasePage(%lld) (rwlatch = %llx)\n", p->id, (long long)p->rwlatch); stasis_buffer_hash_t * bh = bm->impl; @@ -503,6 +509,7 @@ stasis_buffer_manager_t* stasis_buffer_manager_hash_open(stasis_page_handle_t * bm->loadPageImpl = bhLoadPageImpl; bm->loadUninitPageImpl = bhLoadUninitPageImpl; bm->prefetchPages = bhPrefetchPagesImpl; + bm->preallocatePages = bhPreallocatePages; bm->getCachedPageImpl = bhGetCachedPage; bm->releasePageImpl = bhReleasePage; bm->writeBackPage = bhWriteBackPage; diff --git a/src/stasis/bufferManager/concurrentBufferManager.c b/src/stasis/bufferManager/concurrentBufferManager.c index cf0ebcc..c94aa56 100644 --- a/src/stasis/bufferManager/concurrentBufferManager.c +++ b/src/stasis/bufferManager/concurrentBufferManager.c @@ -370,6 +370,10 @@ static void chForcePageRange(stasis_buffer_manager_t *bm, stasis_buffer_manager_ stasis_buffer_concurrent_hash_t * ch = bm->impl; ch->page_handle->force_range(ch->page_handle, start, stop); } +static int chPreallocatePages(stasis_buffer_manager_t * bm, pageid_t start, pageid_t count) { + stasis_buffer_concurrent_hash_t * ch = bm->impl; + return ch->page_handle->preallocate_range(ch->page_handle, start, count); +} static void chBufDeinitHelper(stasis_buffer_manager_t * bm, int crash) { stasis_buffer_concurrent_hash_t *ch = bm->impl; ch->running = 0; @@ -411,6 +415,7 @@ stasis_buffer_manager_t* stasis_buffer_manager_concurrent_hash_open(stasis_page_ bm->loadPageImpl = chLoadPageImpl; bm->loadUninitPageImpl = chLoadUninitPageImpl; bm->prefetchPages = NULL; + bm->preallocatePages = chPreallocatePages; bm->getCachedPageImpl = chGetCachedPage; bm->releasePageImpl = chReleasePage; bm->writeBackPage = chWriteBackPage; diff --git a/src/stasis/bufferManager/legacy/legacyBufferManager.c b/src/stasis/bufferManager/legacy/legacyBufferManager.c index 1a92269..d773547 100644 --- a/src/stasis/bufferManager/legacy/legacyBufferManager.c +++ b/src/stasis/bufferManager/legacy/legacyBufferManager.c @@ -61,6 +61,7 @@ stasis_buffer_manager_t* stasis_buffer_manager_deprecated_open(stasis_page_handl bm->loadPageImpl = bufManLoadPage; bm->loadUninitPageImpl = bufManLoadUninitPage; bm->prefetchPages = NULL; + bm->preallocatePages = NULL; bm->getCachedPageImpl = bufManGetCachedPage; bm->writeBackPage = pageWrite_legacyWrapper; bm->forcePages = forcePageFile_legacyWrapper; diff --git a/src/stasis/bufferManager/pageArray.c b/src/stasis/bufferManager/pageArray.c index ee7b45b..4a3ac2d 100644 --- a/src/stasis/bufferManager/pageArray.c +++ b/src/stasis/bufferManager/pageArray.c @@ -90,6 +90,7 @@ stasis_buffer_manager_t * stasis_buffer_manager_mem_array_open () { bm->loadPageImpl = paLoadPage; bm->loadUninitPageImpl = paLoadUninitPage; bm->prefetchPages = NULL; + bm->preallocatePages = NULL; bm->getCachedPageImpl = paGetCachedPage; bm->writeBackPage = paWriteBackPage; bm->tryToWriteBackPage = paWriteBackPage; diff --git a/src/stasis/flags.c b/src/stasis/flags.c index f67d484..78e3107 100644 --- a/src/stasis/flags.c +++ b/src/stasis/flags.c @@ -33,6 +33,17 @@ int stasis_buffer_manager_io_handle_flags = O_NOATIME; #endif +int stasis_buffer_manager_preallocate_mode = +#ifdef STASIS_BUFFER_MANAGER_PREALLOCATE_MODE + STASIS_BUFFER_MANAGER_PREALLOCATE_MODE; +#else +#ifdef HAVE_POSIX_FALLOCATE + STASIS_BUFFER_MANAGER_PREALLOCATE_DEFAULT; +#else + STASIS_BUFFER_MANAGER_PREALLOCATE_LEGACY; +#endif +#endif + #ifdef STASIS_BUFFER_MANAGER_SIZE pageid_t stasis_buffer_manager_size = STASIS_BUFFER_MANAGER_SIZE; #else // STASIS_BUFFER_MANAGER_SIZE diff --git a/src/stasis/io/file.c b/src/stasis/io/file.c index 8ea45b2..6e74024 100644 --- a/src/stasis/io/file.c +++ b/src/stasis/io/file.c @@ -410,6 +410,16 @@ static int file_force_range(stasis_handle_t *h, lsn_t start, lsn_t stop) { } return ret; } +static int file_fallocate(struct stasis_handle_t* h, lsn_t off, lsn_t len) { + file_impl * impl = h->impl; +#ifdef HAVE_POSIX_FALLOCATE + return posix_fallocate(impl->fd, off, len); +#else + (void)impl; + fprintf(stderr, "file.c: fallocate called, but not supported by this build.\n"); + return -1; +#endif +} struct stasis_handle_t file_func = { .num_copies = file_num_copies, @@ -426,6 +436,7 @@ struct stasis_handle_t file_func = { .release_read_buffer = file_release_read_buffer, .force = file_force, .force_range = file_force_range, + .fallocate = file_fallocate, .error = 0 }; diff --git a/src/stasis/io/pfile.c b/src/stasis/io/pfile.c index 0137ee7..934d17d 100644 --- a/src/stasis/io/pfile.c +++ b/src/stasis/io/pfile.c @@ -342,7 +342,16 @@ static int pfile_force_range(stasis_handle_t *h, lsn_t start, lsn_t stop) { TOCK(force_range_hist); return ret; } - +static int pfile_fallocate(struct stasis_handle_t* h, lsn_t off, lsn_t len) { + pfile_impl * impl = h->impl; +#ifdef HAVE_POSIX_FALLOCATE + return posix_fallocate(impl->fd, off, len); +#else + (void)impl; + fprintf(stderr, "pfile.c: fallocate called, but not supported by this build.\n"); + return -1; +#endif +} static struct stasis_handle_t pfile_func = { .num_copies = pfile_num_copies, .num_copies_buffer = pfile_num_copies_buffer, @@ -358,6 +367,7 @@ static struct stasis_handle_t pfile_func = { .release_read_buffer = pfile_release_read_buffer, .force = pfile_force, .force_range = pfile_force_range, + .fallocate = pfile_fallocate, .error = 0 }; diff --git a/src/stasis/operations/regions.c b/src/stasis/operations/regions.c index c279640..6d5e5fb 100644 --- a/src/stasis/operations/regions.c +++ b/src/stasis/operations/regions.c @@ -270,16 +270,18 @@ static void TregionAllocHelper(int xid, pageid_t page, pageid_t pageCount, int a new_tag.size = PAGEID_T_MAX; - // We're extending the page file, so pre-fill the stasis buffer manager with dirty zeros. This - // prevents the file from becoming sparse. - for(pageid_t i = page+1; i < newPageid; i++) { - Page * p = loadUninitializedPage(xid, i); - writelock(p->rwlatch, 0); - stasis_dirty_page_table_set_dirty(stasis_runtime_dirty_page_table(), p); - unlock(p->rwlatch); - releasePage(p); - } + // We're extending the page file, and need to prevent it from + // becoming sparse. + int preallocation_error + = preallocatePages(page+1, newPageid - page); + if(preallocation_error) { + if(preallocation_error == -1) { + fprintf(stderr, "Note, errno is -1, which usually means that there was a build problem.\n"); + } + perror("Could not preallocate after end of file.\n"); + abort(); + } } new_tag.prev_size = pageCount; // Create the new region, and disassociate it from this transaction immediately. diff --git a/src/stasis/pageHandle.c b/src/stasis/pageHandle.c index e90154e..fe24510 100644 --- a/src/stasis/pageHandle.c +++ b/src/stasis/pageHandle.c @@ -52,6 +52,12 @@ static void phPrefetchRange(stasis_page_handle_t *ph, pageid_t pageid, pageid_t free(buf); } +static int phPreallocateRange(stasis_page_handle_t * ph, pageid_t pageid, pageid_t count) { + lsn_t off = pageid * PAGE_SIZE; + lsn_t len = count * PAGE_SIZE; + + return ((stasis_handle_t*)ph->impl)->fallocate(ph->impl, off, len); +} static void phForce(stasis_page_handle_t * ph) { int err = ((stasis_handle_t*)ph->impl)->force(ph->impl); assert(!err); @@ -91,6 +97,7 @@ stasis_page_handle_t * stasis_page_handle_open(stasis_handle_t * handle, ret->write = phWrite; ret->read = phRead; ret->prefetch_range = phPrefetchRange; + ret->preallocate_range = phPreallocateRange; ret->force_file = phForce; ret->force_range = phForceRange; ret->close = phClose; diff --git a/stasis/bufferManager.h b/stasis/bufferManager.h index 688c173..0311bc8 100644 --- a/stasis/bufferManager.h +++ b/stasis/bufferManager.h @@ -99,7 +99,7 @@ Page * loadUninitializedPage(int xid, pageid_t pageid); Page * loadPageForOperation(int xid, pageid_t pageid, int op); void prefetchPages(pageid_t pageid, pageid_t count); - +int preallocatePages(pageid_t pageid, pageid_t count); /** Get a page from cache. This function should never block on I/O. @@ -126,6 +126,7 @@ struct stasis_buffer_manager_t { Page * (*loadPageImpl)(stasis_buffer_manager_t*, stasis_buffer_manager_handle_t* h, int xid, pageid_t pageid, pagetype_t type); Page * (*loadUninitPageImpl)(stasis_buffer_manager_t*, int xid, pageid_t pageid); void (*prefetchPages)(stasis_buffer_manager_t*, pageid_t pageid, pageid_t count); + int (*preallocatePages)(stasis_buffer_manager_t*, pageid_t pageid, pageid_t count); Page * (*getCachedPageImpl)(stasis_buffer_manager_t*, int xid, const pageid_t pageid); void (*releasePageImpl)(stasis_buffer_manager_t*, Page * p); /** diff --git a/stasis/constants.h b/stasis/constants.h index eae95c7..3ca957f 100644 --- a/stasis/constants.h +++ b/stasis/constants.h @@ -81,6 +81,10 @@ terms specified in this license. #define PERSISTENT 1 #define DURABLE 2 +#define STASIS_BUFFER_MANAGER_PREALLOCATE_DEFAULT 1 +#define STASIS_BUFFER_MANAGER_PREALLOCATE_DISABLED 2 +#define STASIS_BUFFER_MANAGER_PREALLOCATE_LEGACY 3 + #define STASIS_REPLACEMENT_POLICY_THREADSAFE_LRU 1 #define STASIS_REPLACEMENT_POLICY_CONCURRENT_LRU 2 #define STASIS_REPLACEMENT_POLICY_CLOCK 3 diff --git a/stasis/flags.h b/stasis/flags.h index fbfed16..eb07077 100644 --- a/stasis/flags.h +++ b/stasis/flags.h @@ -84,6 +84,15 @@ extern stasis_handle_t* (*stasis_non_blocking_handle_file_factory)(const char* f defining STASIS_BUFFER_MANAGER_IO_HANDLE_FLAGS. */ extern int stasis_buffer_manager_io_handle_flags; +/** + How should stasis grow the page file? Valid options are: + + * STASIS_BUFFER_MANAGER_PREALLOCATE_DISABLED, which avoids any explicit preallocation. (This can cause terrible fragmentation on filesystems that support large files.), + * STASIS_BUFFER_MANAGER_PREALLCOATE_LEGACY, which placed dirty zero filled pages in the buffer cache. (This can cause double writes if the extended region does not fit in RAM, and unnecessarily evicts stuff.) + * STASIS_BUFFER_MANAGER_PREALLOCATE_DEFAULT, the recommended mode, which currently calls posix_fallocate(). This is not supported by old versions of Linux, so we attempt to fallback on the legacy mode at compile time. +*/ +extern int stasis_buffer_manager_preallocate_mode; + /** The default replacement policy. diff --git a/stasis/io/handle.h b/stasis/io/handle.h index ef42a84..36979c6 100644 --- a/stasis/io/handle.h +++ b/stasis/io/handle.h @@ -184,6 +184,7 @@ typedef struct stasis_handle_t { */ int (*force)(struct stasis_handle_t * h); int (*force_range)(struct stasis_handle_t * h, lsn_t start, lsn_t stop); + int (*fallocate)(struct stasis_handle_t * h, lsn_t off, lsn_t len); /** The handle's error flag; this passes errors to the caller when they can't be returned directly. diff --git a/stasis/pageHandle.h b/stasis/pageHandle.h index d46094e..228301a 100644 --- a/stasis/pageHandle.h +++ b/stasis/pageHandle.h @@ -68,8 +68,11 @@ struct stasis_page_handle_t { /** * @param start the pageid of the first page to be forced to disk. * @param stop the pageid of the page after the last page to be forced to disk. + * + * @todo force_range takes a start and a stop page, but the others in pageHandle take an offset and count. */ void (*force_range)(struct stasis_page_handle_t* ph, lsn_t start, lsn_t stop); + int (*preallocate_range)(struct stasis_page_handle_t* ph, lsn_t pageid, lsn_t count); /** Force the page file to disk, then close it. */