add support for fallocate(); enable it by default if it is available at build time.
This commit is contained in:
parent
9083bca1c3
commit
08c8be07b1
17 changed files with 107 additions and 11 deletions
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
Loading…
Reference in a new issue