add support for fallocate(); enable it by default if it is available at build time.

This commit is contained in:
Sears Russell 2011-08-25 21:29:51 +00:00
parent 9083bca1c3
commit 08c8be07b1
17 changed files with 107 additions and 11 deletions

View file

@ -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)

View file

@ -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

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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
};

View file

@ -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
};

View file

@ -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.

View file

@ -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;

View file

@ -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);
/**

View file

@ -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

View file

@ -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.

View file

@ -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.

View file

@ -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.
*/