2009-05-20 21:23:51 +00:00
|
|
|
/*
|
|
|
|
* dirtyPageTable.c
|
|
|
|
*
|
|
|
|
* Created on: May 18, 2009
|
|
|
|
* Author: sears
|
|
|
|
*/
|
|
|
|
|
2009-08-07 18:27:52 +00:00
|
|
|
#include <stasis/redblack.h>
|
2009-05-20 21:23:51 +00:00
|
|
|
#include <stasis/common.h>
|
2009-08-22 00:01:02 +00:00
|
|
|
#include <stasis/flags.h>
|
2009-05-20 21:23:51 +00:00
|
|
|
#include <stasis/dirtyPageTable.h>
|
|
|
|
#include <stasis/page.h>
|
|
|
|
#include <stasis/bufferManager.h>
|
|
|
|
|
2009-08-07 18:27:52 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
pageid_t p;
|
|
|
|
lsn_t lsn;
|
|
|
|
} dpt_entry;
|
|
|
|
|
|
|
|
static int dpt_cmp(const void *ap, const void * bp, const void * ignored) {
|
|
|
|
const dpt_entry * a = ap;
|
|
|
|
const dpt_entry * b = bp;
|
|
|
|
|
|
|
|
return a->p < b->p ? -1 : (a->p == b->p ? 0 : 1);
|
|
|
|
}
|
|
|
|
|
2009-05-20 21:23:51 +00:00
|
|
|
struct stasis_dirty_page_table_t {
|
2009-08-07 18:27:52 +00:00
|
|
|
struct rbtree * table;
|
2009-08-11 17:23:43 +00:00
|
|
|
pageid_t count;
|
2009-05-20 21:23:51 +00:00
|
|
|
pthread_mutex_t mutex;
|
2009-08-13 23:18:49 +00:00
|
|
|
pthread_cond_t flushDone;
|
|
|
|
int flushing;
|
2009-05-20 21:23:51 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
void stasis_dirty_page_table_set_dirty(stasis_dirty_page_table_t * dirtyPages, Page * p) {
|
2009-08-08 07:59:19 +00:00
|
|
|
assertlocked(p->rwlatch);
|
2009-05-20 21:23:51 +00:00
|
|
|
if(!p->dirty) {
|
|
|
|
p->dirty = 1;
|
2009-08-07 18:27:52 +00:00
|
|
|
dpt_entry * e = malloc(sizeof(*e));
|
|
|
|
e->p = p->id;
|
|
|
|
e->lsn = p->LSN;
|
2009-08-14 06:31:13 +00:00
|
|
|
pthread_mutex_lock(&dirtyPages->mutex);
|
2009-08-07 18:27:52 +00:00
|
|
|
const void * ret = rbsearch(e, dirtyPages->table);
|
|
|
|
assert(ret == e); // otherwise, the entry was already in the table.
|
2009-08-11 17:23:43 +00:00
|
|
|
dirtyPages->count++;
|
2009-08-14 06:31:13 +00:00
|
|
|
pthread_mutex_unlock(&dirtyPages->mutex);
|
|
|
|
#ifdef SANITY_CHECKS
|
2009-08-07 18:27:52 +00:00
|
|
|
} else {
|
2009-08-14 06:31:13 +00:00
|
|
|
pthread_mutex_lock(&dirtyPages->mutex);
|
2009-08-07 18:27:52 +00:00
|
|
|
dpt_entry e = { p->id, 0};
|
|
|
|
assert(rbfind(&e, dirtyPages->table));
|
2009-08-14 06:31:13 +00:00
|
|
|
pthread_mutex_unlock(&dirtyPages->mutex);
|
|
|
|
#endif //SANITY_CHECKS
|
2009-05-20 21:23:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void stasis_dirty_page_table_set_clean(stasis_dirty_page_table_t * dirtyPages, Page * p) {
|
2009-08-13 05:53:48 +00:00
|
|
|
assertlocked(p->rwlatch);
|
2009-08-14 06:31:13 +00:00
|
|
|
if(p->dirty) {
|
|
|
|
pthread_mutex_lock(&dirtyPages->mutex);
|
|
|
|
dpt_entry dummy = {p->id, 0};
|
|
|
|
const dpt_entry * e = rbdelete(&dummy, dirtyPages->table);
|
|
|
|
assert(e);
|
2009-08-07 18:27:52 +00:00
|
|
|
assert(e->p == p->id);
|
|
|
|
assert(p->dirty);
|
|
|
|
p->dirty = 0;
|
|
|
|
free((void*)e);
|
2009-08-11 17:23:43 +00:00
|
|
|
dirtyPages->count--;
|
2009-08-14 06:31:13 +00:00
|
|
|
pthread_mutex_unlock(&dirtyPages->mutex);
|
2009-05-20 21:23:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int stasis_dirty_page_table_is_dirty(stasis_dirty_page_table_t * dirtyPages, Page * p) {
|
|
|
|
int ret;
|
2009-08-13 05:53:48 +00:00
|
|
|
assertlocked(p->rwlatch);
|
|
|
|
|
2009-05-20 21:23:51 +00:00
|
|
|
ret = p->dirty;
|
2009-08-14 06:31:13 +00:00
|
|
|
#ifdef SANITY_CHECKS
|
|
|
|
pthread_mutex_lock(&dirtyPages->mutex);
|
2009-08-07 18:27:52 +00:00
|
|
|
dpt_entry e = { p->id, 0};
|
|
|
|
const void* found = rbfind(&e, dirtyPages->table);
|
|
|
|
assert((found && ret) || !(found||ret));
|
2009-05-20 21:23:51 +00:00
|
|
|
pthread_mutex_unlock(&dirtyPages->mutex);
|
2009-08-14 06:31:13 +00:00
|
|
|
#endif
|
2009-05-20 21:23:51 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
lsn_t stasis_dirty_page_table_minRecLSN(stasis_dirty_page_table_t * dirtyPages) {
|
2009-08-07 18:27:52 +00:00
|
|
|
lsn_t lsn = LSN_T_MAX;
|
2009-05-20 21:23:51 +00:00
|
|
|
pthread_mutex_lock(&dirtyPages->mutex);
|
2009-08-07 18:27:52 +00:00
|
|
|
for(const dpt_entry * e = rbmin(dirtyPages->table);
|
|
|
|
e;
|
|
|
|
e = rblookup(RB_LUGREAT, e, dirtyPages->table)) {
|
|
|
|
if(e->lsn < lsn) {
|
|
|
|
lsn = e->lsn;
|
2009-05-20 21:23:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
pthread_mutex_unlock(&dirtyPages->mutex);
|
|
|
|
return lsn;
|
|
|
|
}
|
|
|
|
|
2009-08-11 17:23:43 +00:00
|
|
|
pageid_t stasis_dirty_page_table_dirty_count(stasis_dirty_page_table_t * dirtyPages) {
|
|
|
|
pthread_mutex_lock(&dirtyPages->mutex);
|
|
|
|
pageid_t ret = dirtyPages->count;
|
2009-08-13 05:53:48 +00:00
|
|
|
assert(dirtyPages->count >= 0);
|
2009-08-11 17:23:43 +00:00
|
|
|
pthread_mutex_unlock(&dirtyPages->mutex);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-08-13 23:18:49 +00:00
|
|
|
int stasis_dirty_page_table_flush(stasis_dirty_page_table_t * dirtyPages) {
|
2009-08-13 05:53:48 +00:00
|
|
|
dpt_entry dummy = { 0, 0 };
|
|
|
|
const int stride = 200;
|
|
|
|
pageid_t vals[stride];
|
|
|
|
int off = 0;
|
|
|
|
int strides = 0;
|
|
|
|
pthread_mutex_lock(&dirtyPages->mutex);
|
2009-08-13 23:18:49 +00:00
|
|
|
if(dirtyPages->flushing) {
|
|
|
|
pthread_cond_wait(&dirtyPages->flushDone, &dirtyPages->mutex);
|
|
|
|
pthread_mutex_unlock(&dirtyPages->mutex);
|
|
|
|
return EAGAIN;
|
|
|
|
}
|
|
|
|
dirtyPages->flushing = 1;
|
2009-08-13 05:53:48 +00:00
|
|
|
for(const dpt_entry * e = rblookup(RB_LUGTEQ, &dummy, dirtyPages->table) ;
|
|
|
|
e;
|
|
|
|
e = rblookup(RB_LUGREAT, &dummy, dirtyPages->table)) {
|
|
|
|
dummy = *e;
|
|
|
|
vals[off] = dummy.p;
|
|
|
|
off++;
|
|
|
|
if(off == stride) {
|
|
|
|
pthread_mutex_unlock(&dirtyPages->mutex);
|
|
|
|
for(pageid_t i = 0; i < off; i++) {
|
|
|
|
writeBackPage(vals[i]);
|
|
|
|
}
|
|
|
|
off = 0;
|
|
|
|
strides++;
|
|
|
|
pthread_mutex_lock(&dirtyPages->mutex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pthread_mutex_unlock(&dirtyPages->mutex);
|
|
|
|
for(int i = 0; i < off; i++) {
|
|
|
|
writeBackPage(vals[i]);
|
|
|
|
}
|
2009-08-13 23:18:49 +00:00
|
|
|
pthread_mutex_lock(&dirtyPages->mutex);
|
|
|
|
dirtyPages->flushing = 0;
|
|
|
|
pthread_cond_broadcast(&dirtyPages->flushDone);
|
|
|
|
pthread_mutex_unlock(&dirtyPages->mutex);
|
|
|
|
|
2009-08-13 05:53:48 +00:00
|
|
|
// if(strides < 5) { DEBUG("strides: %d dirtyCount = %lld\n", strides, stasis_dirty_page_table_dirty_count(dirtyPages)); }
|
2009-08-13 23:18:49 +00:00
|
|
|
|
|
|
|
return 0;
|
2009-05-20 21:23:51 +00:00
|
|
|
}
|
|
|
|
void stasis_dirty_page_table_flush_range(stasis_dirty_page_table_t * dirtyPages, pageid_t start, pageid_t stop) {
|
|
|
|
|
|
|
|
pthread_mutex_lock(&dirtyPages->mutex);
|
2009-08-13 23:18:49 +00:00
|
|
|
int waitCount = 0;
|
|
|
|
while(dirtyPages->flushing) {
|
|
|
|
pthread_cond_wait(&dirtyPages->flushDone, &dirtyPages->mutex);
|
|
|
|
waitCount++;
|
|
|
|
if(waitCount == 2) {
|
|
|
|
// a call to stasis_dirty_page_table_flush was initiated and completed since we were called.
|
|
|
|
pthread_mutex_unlock(&dirtyPages->mutex);
|
|
|
|
return;
|
|
|
|
} // else, a call to flush returned, but that call could have been initiated before we were called...
|
|
|
|
}
|
|
|
|
|
2009-08-07 18:27:52 +00:00
|
|
|
pageid_t * staleDirtyPages = 0;
|
|
|
|
pageid_t n = 0;
|
|
|
|
dpt_entry dummy = { start, 0 };
|
|
|
|
for(const dpt_entry * e = rblookup(RB_LUGTEQ, &dummy, dirtyPages->table);
|
|
|
|
e && (stop == 0 || e->p < stop);
|
|
|
|
e = rblookup(RB_LUGREAT, e, dirtyPages->table)) {
|
|
|
|
n++;
|
|
|
|
staleDirtyPages = realloc(staleDirtyPages, sizeof(pageid_t) * n);
|
|
|
|
staleDirtyPages[n-1] = e->p;
|
2009-05-20 21:23:51 +00:00
|
|
|
}
|
|
|
|
pthread_mutex_unlock(&dirtyPages->mutex);
|
|
|
|
|
2009-08-07 18:27:52 +00:00
|
|
|
for(pageid_t i = 0; i < n; i++) {
|
2009-08-08 01:43:03 +00:00
|
|
|
int err = writeBackPage(staleDirtyPages[i]);
|
|
|
|
if(stop && (err == EBUSY)) { abort(); /*api violation!*/ }
|
2009-05-20 21:23:51 +00:00
|
|
|
}
|
|
|
|
free(staleDirtyPages);
|
2009-08-13 05:53:48 +00:00
|
|
|
// forcePageRange(start*PAGE_SIZE,stop*PAGE_SIZE);
|
2009-05-20 21:23:51 +00:00
|
|
|
}
|
2009-08-07 18:27:52 +00:00
|
|
|
|
2009-05-20 21:23:51 +00:00
|
|
|
stasis_dirty_page_table_t * stasis_dirty_page_table_init() {
|
|
|
|
stasis_dirty_page_table_t * ret = malloc(sizeof(*ret));
|
2009-08-07 18:27:52 +00:00
|
|
|
ret->table = rbinit(dpt_cmp, 0);
|
2009-08-13 05:53:48 +00:00
|
|
|
ret->count = 0;
|
2009-05-20 21:23:51 +00:00
|
|
|
pthread_mutex_init(&ret->mutex, 0);
|
2009-08-13 23:18:49 +00:00
|
|
|
pthread_cond_init(&ret->flushDone, 0);
|
|
|
|
ret->flushing = 0;
|
2009-05-20 21:23:51 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void stasis_dirty_page_table_deinit(stasis_dirty_page_table_t * dirtyPages) {
|
|
|
|
int areDirty = 0;
|
2009-08-07 18:27:52 +00:00
|
|
|
dpt_entry dummy = {0, 0};
|
|
|
|
for(const dpt_entry * e = rblookup(RB_LUGTEQ, &dummy, dirtyPages->table);
|
|
|
|
e;
|
|
|
|
e = rblookup(RB_LUGREAT, &dummy, dirtyPages->table)) {
|
|
|
|
|
2009-05-20 21:23:51 +00:00
|
|
|
if((!areDirty) &&
|
|
|
|
(!stasis_suppress_unclean_shutdown_warnings)) {
|
|
|
|
printf("Warning: dirtyPagesDeinit detected dirty, unwritten pages. "
|
|
|
|
"Updates lost?\n");
|
|
|
|
areDirty = 1;
|
|
|
|
}
|
2009-08-07 18:27:52 +00:00
|
|
|
dummy = *e;
|
|
|
|
rbdelete(e, dirtyPages->table);
|
|
|
|
free((void*)e);
|
2009-05-20 21:23:51 +00:00
|
|
|
}
|
2009-08-07 18:27:52 +00:00
|
|
|
|
|
|
|
rbdestroy(dirtyPages->table);
|
2009-05-20 21:23:51 +00:00
|
|
|
pthread_mutex_destroy(&dirtyPages->mutex);
|
|
|
|
free(dirtyPages);
|
|
|
|
}
|