2004-08-21 00:03:30 +00:00
|
|
|
#define _XOPEN_SOURCE 600
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
2007-06-07 21:53:09 +00:00
|
|
|
#include "config.h"
|
2008-04-13 04:02:57 +00:00
|
|
|
#include <stasis/page.h>
|
2007-06-11 21:36:57 +00:00
|
|
|
#include <stasis/operations/pageOperations.h>
|
2004-08-03 02:04:56 +00:00
|
|
|
#include <assert.h>
|
2005-05-05 21:34:12 +00:00
|
|
|
#include <alloca.h>
|
2004-08-21 00:03:30 +00:00
|
|
|
|
|
|
|
static pthread_mutex_t pageAllocMutex;
|
|
|
|
|
2008-09-28 03:11:24 +00:00
|
|
|
static int op_page_set_range(const LogEntry* e, Page* p) {
|
|
|
|
assert(e->update.arg_size >= sizeof(int));
|
|
|
|
assert(!((e->update.arg_size - sizeof(int)) % 2));
|
|
|
|
|
|
|
|
int off = *(int*)getUpdateArgs(e);
|
|
|
|
int len = (e->update.arg_size - sizeof(int)) >> 1;
|
|
|
|
|
|
|
|
assert(off+len <=PAGE_SIZE);
|
|
|
|
|
2009-04-03 22:01:37 +00:00
|
|
|
memcpy(p->memAddr + off, ((const byte*)getUpdateArgs(e))+sizeof(int), len);
|
2008-09-28 03:11:24 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static int op_page_set_range_inverse(const LogEntry* e, Page* p) {
|
|
|
|
assert(e->update.arg_size >= sizeof(int));
|
|
|
|
assert(!((e->update.arg_size - sizeof(int)) % 2));
|
|
|
|
|
|
|
|
int off = *(int*)getUpdateArgs(e);
|
|
|
|
int len = (e->update.arg_size - sizeof(int)) >> 1;
|
|
|
|
|
|
|
|
assert(off+len <=PAGE_SIZE);
|
|
|
|
|
2009-04-03 22:01:37 +00:00
|
|
|
memcpy(p->memAddr + off, ((const byte*)getUpdateArgs(e))+sizeof(int)+len,
|
|
|
|
len);
|
2004-08-03 02:04:56 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2004-08-21 00:03:30 +00:00
|
|
|
|
2008-10-03 02:42:25 +00:00
|
|
|
compensated_function int TpageGet(int xid, pageid_t page, void *memAddr) {
|
2005-02-22 03:10:54 +00:00
|
|
|
Page * q = 0;
|
|
|
|
try_ret(compensation_error()) {
|
2008-10-03 02:42:25 +00:00
|
|
|
q = loadPage(xid, page);
|
2005-02-22 03:10:54 +00:00
|
|
|
memcpy(memAddr, q->memAddr, PAGE_SIZE);
|
|
|
|
} end_ret(compensation_error());
|
2006-07-25 01:03:57 +00:00
|
|
|
try_ret(compensation_error()) {
|
|
|
|
releasePage(q);
|
|
|
|
} end_ret(compensation_error());
|
2004-08-21 00:03:30 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-10-03 02:42:25 +00:00
|
|
|
compensated_function int TpageSet(int xid, pageid_t page, const void * memAddr) {
|
|
|
|
return TpageSetRange(xid, page, 0, memAddr, PAGE_SIZE);
|
2008-09-28 03:11:24 +00:00
|
|
|
}
|
|
|
|
|
2008-10-03 02:42:25 +00:00
|
|
|
int TpageSetRange(int xid, pageid_t page, int offset, const void * memAddr, int len) {
|
2008-09-28 03:11:24 +00:00
|
|
|
// XXX need to pack offset into front of log entry
|
|
|
|
|
2008-10-03 02:42:25 +00:00
|
|
|
Page * p = loadPage(xid, page);
|
2008-09-28 03:11:24 +00:00
|
|
|
byte * logArg = malloc(sizeof(int) + 2 * len);
|
|
|
|
|
|
|
|
*(int*)logArg = offset;
|
|
|
|
memcpy(logArg+sizeof(int), ((const byte*)memAddr), len);
|
|
|
|
memcpy(logArg+sizeof(int)+len, p->memAddr+offset, len);
|
|
|
|
|
2008-10-09 20:14:55 +00:00
|
|
|
releasePage(p);
|
|
|
|
|
2008-09-28 03:11:24 +00:00
|
|
|
try_ret(compensation_error()) {
|
2008-10-03 02:42:25 +00:00
|
|
|
Tupdate(xid,page,logArg,sizeof(int)+len*2,OPERATION_PAGE_SET_RANGE);
|
2005-02-22 03:10:54 +00:00
|
|
|
} end_ret(compensation_error());
|
2008-09-28 03:11:24 +00:00
|
|
|
|
|
|
|
free(logArg);
|
2004-08-03 02:04:56 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-06-01 21:06:18 +00:00
|
|
|
/** @todo region sizes should be dynamic. */
|
2006-07-21 01:07:09 +00:00
|
|
|
#define TALLOC_PAGE_REGION_SIZE 128 // 512K
|
2004-08-21 00:03:30 +00:00
|
|
|
|
2006-07-20 01:29:39 +00:00
|
|
|
/**
|
|
|
|
This calls loadPage and releasePage directly, and bypasses the
|
|
|
|
logger.
|
|
|
|
*/
|
2005-02-22 03:10:54 +00:00
|
|
|
compensated_function void pageOperationsInit() {
|
2006-07-20 01:29:39 +00:00
|
|
|
|
|
|
|
regionsInit();
|
2004-08-21 00:03:30 +00:00
|
|
|
|
2006-07-21 01:07:09 +00:00
|
|
|
boundary_tag t;
|
|
|
|
recordid rid = {0, 0, sizeof(boundary_tag)};
|
|
|
|
// Need to find a region with some free pages in it.
|
|
|
|
Tread(-1, rid, &t);
|
|
|
|
|
|
|
|
|
2004-08-21 00:03:30 +00:00
|
|
|
pthread_mutex_init(&pageAllocMutex, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-10-03 02:42:25 +00:00
|
|
|
compensated_function int TpageDealloc(int xid, pageid_t page) {
|
|
|
|
TregionDealloc(xid, page); // @todo inefficient hack!
|
2004-08-03 02:04:56 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-10-03 02:42:25 +00:00
|
|
|
compensated_function pageid_t TpageAlloc(int xid) {
|
2006-07-20 01:29:39 +00:00
|
|
|
return TregionAlloc(xid, 1, STORAGE_MANAGER_NAIVE_PAGE_ALLOC);
|
2004-08-03 02:04:56 +00:00
|
|
|
}
|
2004-08-21 00:03:30 +00:00
|
|
|
|
2005-05-05 21:34:12 +00:00
|
|
|
/**
|
2008-10-03 02:42:25 +00:00
|
|
|
@return a pageid_t. The page field contains the page that was
|
2005-05-05 21:34:12 +00:00
|
|
|
allocated, the slot field contains the number of slots on the
|
|
|
|
apge, and the size field contains the size of each slot.
|
|
|
|
*/
|
2008-10-03 02:42:25 +00:00
|
|
|
pageid_t TfixedPageAlloc(int xid, int size) {
|
|
|
|
pageid_t page = TpageAlloc(xid);
|
2009-03-31 05:02:54 +00:00
|
|
|
TinitializeFixedPage(xid, page, size);
|
2008-10-03 02:42:25 +00:00
|
|
|
return page;
|
2005-05-05 21:34:12 +00:00
|
|
|
}
|
|
|
|
|
2008-10-03 02:42:25 +00:00
|
|
|
compensated_function pageid_t TpageAllocMany(int xid, int count) {
|
2006-07-20 01:29:39 +00:00
|
|
|
return TregionAlloc(xid, count, STORAGE_MANAGER_NAIVE_PAGE_ALLOC);
|
2006-07-25 22:22:49 +00:00
|
|
|
}
|
|
|
|
|
2008-10-03 02:42:25 +00:00
|
|
|
int TpageGetType(int xid, pageid_t page) {
|
|
|
|
Page * p = loadPage(xid, page);
|
2007-10-02 00:18:33 +00:00
|
|
|
int ret = *stasis_page_type_ptr(p);
|
2006-07-25 22:22:49 +00:00
|
|
|
releasePage(p);
|
|
|
|
return ret;
|
2004-08-03 02:04:56 +00:00
|
|
|
}
|
|
|
|
|
2004-08-21 00:03:30 +00:00
|
|
|
/** Safely allocating and freeing pages is suprisingly complex. Here is a summary of the process:
|
|
|
|
|
|
|
|
Alloc:
|
|
|
|
|
|
|
|
obtain mutex
|
|
|
|
choose a free page using in-memory data
|
|
|
|
load page to be used, and update in-memory data. (obtains lock on loaded page)
|
2005-02-22 03:10:54 +00:00
|
|
|
T update() the page, zeroing it, and saving the old successor in the log.
|
2004-08-21 00:03:30 +00:00
|
|
|
relase the page (avoid deadlock in next step)
|
2005-02-22 03:10:54 +00:00
|
|
|
T update() LLADD's header page (the first in the store file) with a new copy of
|
2004-08-21 00:03:30 +00:00
|
|
|
the in-memory data, saving old version in the log.
|
|
|
|
release mutex
|
|
|
|
|
|
|
|
Free:
|
|
|
|
|
|
|
|
obtain mutex
|
|
|
|
determine the current head of the freelist using in-memory data
|
2005-02-22 03:10:54 +00:00
|
|
|
T update() the page, initializing it to be a freepage, and physically logging the old version
|
2004-08-21 00:03:30 +00:00
|
|
|
release the page
|
2005-02-22 03:10:54 +00:00
|
|
|
T update() LLADD's header page with a new copy of the in-memory data, saving old version in the log
|
2004-08-21 00:03:30 +00:00
|
|
|
release mutex
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
/** frees a page by zeroing it, setting its type to LLADD_FREE_PAGE,
|
|
|
|
and setting the successor pointer. This operation physically logs
|
|
|
|
a whole page, which makes it expensive. Doing so is necessary in
|
|
|
|
general, but it is possible that application specific logic could
|
2006-07-20 01:29:39 +00:00
|
|
|
avoid the physical logging here.
|
|
|
|
|
|
|
|
Instead, we should just record the fact that the page was freed
|
|
|
|
somewhere. That way, we don't need to read the page in, or write
|
|
|
|
out information about it. If we lock the page against
|
|
|
|
reallocation until the current transaction commits, then we're
|
|
|
|
fine.
|
2004-08-03 02:04:56 +00:00
|
|
|
|
2006-07-20 01:29:39 +00:00
|
|
|
*/
|
2004-08-03 02:04:56 +00:00
|
|
|
|
2009-03-31 05:02:54 +00:00
|
|
|
stasis_operation_impl stasis_op_impl_page_set_range() {
|
|
|
|
stasis_operation_impl o = {
|
|
|
|
OPERATION_PAGE_SET_RANGE,
|
2008-09-28 03:11:24 +00:00
|
|
|
OPERATION_PAGE_SET_RANGE,
|
|
|
|
OPERATION_PAGE_SET_RANGE_INVERSE,
|
|
|
|
op_page_set_range
|
|
|
|
};
|
|
|
|
return o;
|
|
|
|
}
|
|
|
|
|
2009-03-31 05:02:54 +00:00
|
|
|
stasis_operation_impl stasis_op_impl_page_set_range_inverse() {
|
|
|
|
stasis_operation_impl o = {
|
|
|
|
OPERATION_PAGE_SET_RANGE_INVERSE,
|
2008-09-28 03:11:24 +00:00
|
|
|
OPERATION_PAGE_SET_RANGE_INVERSE,
|
|
|
|
OPERATION_PAGE_SET_RANGE,
|
|
|
|
&op_page_set_range_inverse
|
2004-08-03 02:04:56 +00:00
|
|
|
};
|
|
|
|
return o;
|
|
|
|
}
|
2009-03-31 05:02:54 +00:00
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
slotid_t slot;
|
|
|
|
int64_t type;
|
|
|
|
} page_init_arg;
|
|
|
|
|
|
|
|
|
|
|
|
void TinitializeSlottedPage(int xid, pageid_t page) {
|
|
|
|
page_init_arg a = { SLOTTED_PAGE, 0 };
|
|
|
|
Tupdate(xid, page, &a, sizeof(a), OPERATION_INITIALIZE_PAGE);
|
|
|
|
}
|
|
|
|
void TinitializeFixedPage(int xid, pageid_t page, int slotLength) {
|
|
|
|
page_init_arg a = { FIXED_PAGE, slotLength };
|
|
|
|
Tupdate(xid, page, &a, sizeof(a), OPERATION_INITIALIZE_PAGE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int op_initialize_page(const LogEntry* e, Page* p) {
|
|
|
|
assert(e->update.arg_size == sizeof(page_init_arg));
|
|
|
|
const page_init_arg* arg = (const page_init_arg*)getUpdateArgs(e);
|
|
|
|
|
|
|
|
switch(arg->slot) {
|
|
|
|
case SLOTTED_PAGE:
|
|
|
|
stasis_slotted_initialize_page(p);
|
|
|
|
break;
|
|
|
|
case FIXED_PAGE:
|
|
|
|
stasis_fixed_initialize_page(p, arg->type,
|
|
|
|
stasis_fixed_records_per_page
|
|
|
|
(stasis_record_type_to_size(arg->type)));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
stasis_operation_impl stasis_op_impl_page_initialize() {
|
|
|
|
stasis_operation_impl o = {
|
|
|
|
OPERATION_INITIALIZE_PAGE,
|
|
|
|
OPERATION_INITIALIZE_PAGE,
|
|
|
|
OPERATION_NOOP,
|
|
|
|
op_initialize_page
|
|
|
|
};
|
|
|
|
return o;
|
|
|
|
}
|