mm: refactor cache.c to snake case

This commit is contained in:
Matheus
2026-07-01 12:38:02 -03:00
parent d890eb5fb2
commit 9c5854d645
+103 -91
View File
@@ -12,10 +12,10 @@
static mutex_t mutex; static mutex_t mutex;
static page_t **table; static page_t **table;
static thread_t *writerthread; static thread_t *writer_thread;
static semaphore_t sync; static semaphore_t sync;
static eventheader_t syncevent; static eventheader_t sync_event;
static eventheader_t pagereadyevent; static eventheader_t page_ready_event;
size_t mm_cache_cached_pages; size_t mm_cache_cached_pages;
#define HOLD_LOCK() \ #define HOLD_LOCK() \
@@ -24,8 +24,20 @@ size_t mm_cache_cached_pages;
#define RELEASE_LOCK() \ #define RELEASE_LOCK() \
MUTEX_RELEASE(&mutex); MUTEX_RELEASE(&mutex);
static inline uint64_t fnv1ahash(void *buffer, size_t size); static inline uint64_t fnv1a_hash(void *buffer, size_t size) {
static uintmax_t getentry(vnode_t *vnode, uintmax_t offset) { uint8_t *ptr = buffer;
uint8_t *top = ptr + size;
uint64_t h = FNV1OFFSET;
while (ptr < top) {
h ^= *ptr++;
h *= FNV1PRIME;
}
return h;
}
static uintmax_t get_entry(vnode_t *vnode, uintmax_t offset) {
struct { struct {
vnode_t *vnode; vnode_t *vnode;
uintmax_t offset; uintmax_t offset;
@@ -34,12 +46,12 @@ static uintmax_t getentry(vnode_t *vnode, uintmax_t offset) {
tmp.vnode = vnode; tmp.vnode = vnode;
tmp.offset = offset; tmp.offset = offset;
return fnv1ahash(&tmp, sizeof(tmp)) % TABLE_SIZE; return fnv1a_hash(&tmp, sizeof(tmp)) % TABLE_SIZE;
} }
// assumes lock is held // assumes lock is held
static page_t *findpage(vnode_t *vnode, uintmax_t offset) { static page_t *find_page(vnode_t *vnode, uintmax_t offset) {
uintmax_t entry = getentry(vnode, offset); uintmax_t entry = get_entry(vnode, offset);
page_t *page = table[entry]; page_t *page = table[entry];
while (page) { while (page) {
@@ -52,8 +64,8 @@ static page_t *findpage(vnode_t *vnode, uintmax_t offset) {
} }
// assumes lock is held // assumes lock is held
static void putpage(page_t *page) { static void put_page(page_t *page) {
uintmax_t entry = getentry(page->backing, page->offset); uintmax_t entry = get_entry(page->backing, page->offset);
// add to table list // add to table list
page->hash_prev = NULL; page->hash_prev = NULL;
page->hash_next = table[entry]; page->hash_next = table[entry];
@@ -74,8 +86,8 @@ static void putpage(page_t *page) {
} }
// assumes lock is held // assumes lock is held
static void removepage(page_t *page) { static void remove_page(page_t *page) {
uintmax_t entry = getentry(page->backing, page->offset); uintmax_t entry = get_entry(page->backing, page->offset);
// remove from table list // remove from table list
if (page->hash_next) if (page->hash_next)
@@ -109,8 +121,8 @@ int mm_cache_get_page(vnode_t *vnode, uintmax_t offset, page_t **res) {
retry_err: retry_err:
HOLD_LOCK(); HOLD_LOCK();
page_t *newpage = NULL; page_t *new_page = NULL;
volatile page_t *page = findpage(vnode, offset); volatile page_t *page = find_page(vnode, offset);
retry: retry:
if (page) { if (page) {
// page is present in the page cache // page is present in the page cache
@@ -118,12 +130,12 @@ int mm_cache_get_page(vnode_t *vnode, uintmax_t offset, page_t **res) {
RELEASE_LOCK(); RELEASE_LOCK();
// in the case of a retry, release the allocated page here // in the case of a retry, release the allocated page here
if (newpage) if (new_page)
mm_release_page(mm_get_page_address(newpage)); mm_release_page(mm_get_page_address(new_page));
eventlistener_t listener; eventlistener_t listener;
EVENT_INITLISTENER(&listener); EVENT_INITLISTENER(&listener);
EVENT_ATTACH(&listener, &pagereadyevent); EVENT_ATTACH(&listener, &page_ready_event);
// wait for page to be ready // wait for page to be ready
while ((page->flags & (PAGE_FLAGS_READY | PAGE_FLAGS_ERROR)) == 0) while ((page->flags & (PAGE_FLAGS_READY | PAGE_FLAGS_ERROR)) == 0)
@@ -146,26 +158,26 @@ int mm_cache_get_page(vnode_t *vnode, uintmax_t offset, page_t **res) {
if (address == NULL) if (address == NULL)
return ENOMEM; return ENOMEM;
newpage = mm_get_page(address); new_page = mm_get_page(address);
HOLD_LOCK(); HOLD_LOCK();
// while the lock wasn't being held, the page could have potentially been added to the cache // while the lock wasn't being held, the page could have potentially been added to the cache
// check for it again and return from the function as if it was always there in the first place // check for it again and return from the function as if it was always there in the first place
page = findpage(vnode, offset); page = find_page(vnode, offset);
if (page) if (page)
goto retry; goto retry;
newpage->backing = vnode; new_page->backing = vnode;
newpage->offset = offset; new_page->offset = offset;
// add it to the page cache // add it to the page cache
putpage(newpage); put_page(new_page);
RELEASE_LOCK(); RELEASE_LOCK();
VOP_LOCK(vnode); VOP_LOCK(vnode);
int error = VOP_GETPAGE(vnode, offset, newpage); int error = VOP_GETPAGE(vnode, offset, new_page);
VOP_UNLOCK(vnode); VOP_UNLOCK(vnode);
if (error) { if (error) {
@@ -173,24 +185,24 @@ int mm_cache_get_page(vnode_t *vnode, uintmax_t offset, page_t **res) {
// tell the sleeping threads that something happened and free the page // tell the sleeping threads that something happened and free the page
// by setting backing to null so it gets treated as an anonymous page again // by setting backing to null so it gets treated as an anonymous page again
HOLD_LOCK(); HOLD_LOCK();
removepage(newpage); remove_page(new_page);
newpage->flags |= PAGE_FLAGS_ERROR; new_page->flags |= PAGE_FLAGS_ERROR;
newpage->backing = NULL; new_page->backing = NULL;
newpage->offset = 0; new_page->offset = 0;
RELEASE_LOCK(); RELEASE_LOCK();
mm_release_page(mm_get_page_address(newpage)); mm_release_page(mm_get_page_address(new_page));
EVENT_SIGNAL(&pagereadyevent); EVENT_SIGNAL(&page_ready_event);
return error; return error;
} }
HOLD_LOCK(); HOLD_LOCK();
newpage->flags |= PAGE_FLAGS_READY; new_page->flags |= PAGE_FLAGS_READY;
RELEASE_LOCK(); RELEASE_LOCK();
EVENT_SIGNAL(&pagereadyevent); EVENT_SIGNAL(&page_ready_event);
*res = newpage; *res = new_page;
} }
return 0; return 0;
@@ -201,8 +213,8 @@ int mm_cache_push_page(vnode_t *vnode, uintmax_t offset, page_t *page) {
__assert((offset % PAGE_SIZE) == 0); __assert((offset % PAGE_SIZE) == 0);
HOLD_LOCK(); HOLD_LOCK();
page_t *pagetest = findpage(vnode, offset); page_t *page_test = find_page(vnode, offset);
if (pagetest) { if (page_test) {
RELEASE_LOCK(); RELEASE_LOCK();
return EAGAIN; return EAGAIN;
} }
@@ -211,7 +223,7 @@ int mm_cache_push_page(vnode_t *vnode, uintmax_t offset, page_t *page) {
page->offset = offset; page->offset = offset;
page->flags |= PAGE_FLAGS_READY; page->flags |= PAGE_FLAGS_READY;
putpage(page); put_page(page);
RELEASE_LOCK(); RELEASE_LOCK();
return 0; return 0;
@@ -229,7 +241,7 @@ int mm_cache_evict(page_t *page) {
if ((page->flags & PAGE_FLAGS_TRUNCATED) == 0) { if ((page->flags & PAGE_FLAGS_TRUNCATED) == 0) {
// the page needs to be removed from the cache to continue // the page needs to be removed from the cache to continue
removepage(page); remove_page(page);
} }
page->flags = 0; page->flags = 0;
@@ -256,7 +268,7 @@ int mm_cache_take_page(page_t *page) {
// XXX maybe just not allow truncated pages to take up space like this? // XXX maybe just not allow truncated pages to take up space like this?
if ((page->flags & PAGE_FLAGS_TRUNCATED) == 0) { if ((page->flags & PAGE_FLAGS_TRUNCATED) == 0) {
// the page needs to be removed from the cache to continue // the page needs to be removed from the cache to continue
removepage(page); remove_page(page);
} }
RELEASE_LOCK(); RELEASE_LOCK();
@@ -265,29 +277,29 @@ int mm_cache_take_page(page_t *page) {
int mm_cache_truncate(vnode_t *vnode, uintmax_t offset) { int mm_cache_truncate(vnode_t *vnode, uintmax_t offset) {
HOLD_LOCK(); HOLD_LOCK();
page_t *pagelist = NULL; page_t *page_list = NULL;
page_t *page = vnode->pages; page_t *page = vnode->pages;
while (page) { while (page) {
page_t *oldpage = page; page_t *old_page = page;
page = page->vnode_next; page = page->vnode_next;
// only truncate past a certain offset // only truncate past a certain offset
if (oldpage->offset < offset) if (old_page->offset < offset)
continue; continue;
oldpage->flags |= PAGE_FLAGS_TRUNCATED; old_page->flags |= PAGE_FLAGS_TRUNCATED;
removepage(oldpage); remove_page(old_page);
oldpage->vnode_next = pagelist; old_page->vnode_next = page_list;
pagelist = oldpage; page_list = old_page;
} }
RELEASE_LOCK(); RELEASE_LOCK();
// make sure to unref if they are pinned // make sure to unref if they are pinned
while (pagelist) { while (page_list) {
page_t *page = pagelist; page_t *page = page_list;
pagelist = pagelist->vnode_next; page_list = page_list->vnode_next;
if (page->flags & PAGE_FLAGS_PINNED) if (page->flags & PAGE_FLAGS_PINNED)
mm_release_page(mm_get_page_address(page)); mm_release_page(mm_get_page_address(page));
} }
@@ -298,18 +310,18 @@ int mm_cache_truncate(vnode_t *vnode, uintmax_t offset) {
// called with lock held // called with lock held
// returns with lock released // returns with lock released
// expects backing lock to be held // expects backing lock to be held
static int syncpage(page_t *page, bool backinglock) { static int sync_page(page_t *page, bool backing_lock) {
__assert(page->flags & PAGE_FLAGS_DIRTY); __assert(page->flags & PAGE_FLAGS_DIRTY);
page->flags &= ~PAGE_FLAGS_DIRTY; page->flags &= ~PAGE_FLAGS_DIRTY;
RELEASE_LOCK(); RELEASE_LOCK();
int e = 0; int e = 0;
if ((page->flags & PAGE_FLAGS_TRUNCATED) == 0) { if ((page->flags & PAGE_FLAGS_TRUNCATED) == 0) {
if (backinglock) if (backing_lock)
VOP_LOCK(page->backing); VOP_LOCK(page->backing);
e = VOP_PUTPAGE(page->backing, page->offset, (page_t *)page); e = VOP_PUTPAGE(page->backing, page->offset, (page_t *)page);
if (backinglock) if (backing_lock)
VOP_UNLOCK(page->backing); VOP_UNLOCK(page->backing);
VOP_RELEASE(page->backing); VOP_RELEASE(page->backing);
} else { } else {
@@ -322,8 +334,8 @@ static int syncpage(page_t *page, bool backinglock) {
return e; return e;
} }
static page_t *dirtylist; static page_t *dirty_list;
static page_t *dirtylistend; static page_t *dirty_list_end;
// expects vnode to be held // expects vnode to be held
int mm_cache_sync_vnode(vnode_t *vnode, uintmax_t offset, size_t size) { int mm_cache_sync_vnode(vnode_t *vnode, uintmax_t offset, size_t size) {
@@ -336,7 +348,7 @@ int mm_cache_sync_vnode(vnode_t *vnode, uintmax_t offset, size_t size) {
// loop through all vnode pages in memory and check which ones are in the range and are dirty // loop through all vnode pages in memory and check which ones are in the range and are dirty
// TODO create a proper vnode dirty list as to not have to loop through the ENTIRE thing in memory // TODO create a proper vnode dirty list as to not have to loop through the ENTIRE thing in memory
page_t *page = vnode->pages; page_t *page = vnode->pages;
page_t *vnodedirtylist = NULL; page_t *vnode_dirty_list = NULL;
for (; page; page = page->vnode_next) { for (; page; page = page->vnode_next) {
if (page->offset < offset || page->offset >= top || (page->flags & PAGE_FLAGS_DIRTY) == 0 || (page->flags & PAGE_FLAGS_VNODE_SYNCING)) if (page->offset < offset || page->offset >= top || (page->flags & PAGE_FLAGS_DIRTY) == 0 || (page->flags & PAGE_FLAGS_VNODE_SYNCING))
continue; continue;
@@ -346,85 +358,85 @@ int mm_cache_sync_vnode(vnode_t *vnode, uintmax_t offset, size_t size) {
if (page->write_next) if (page->write_next)
page->write_next->write_prev = page->write_prev; page->write_next->write_prev = page->write_prev;
else else
dirtylistend = page->write_prev; dirty_list_end = page->write_prev;
if (page->write_prev) if (page->write_prev)
page->write_prev->write_next = page->write_next; page->write_prev->write_next = page->write_next;
else else
dirtylist = page->write_next; dirty_list = page->write_next;
page->write_next = vnodedirtylist; page->write_next = vnode_dirty_list;
page->write_prev = NULL; page->write_prev = NULL;
page->flags |= PAGE_FLAGS_VNODE_SYNCING; page->flags |= PAGE_FLAGS_VNODE_SYNCING;
vnodedirtylist = page; vnode_dirty_list = page;
} }
RELEASE_LOCK(); RELEASE_LOCK();
int e = 0; int e = 0;
while (vnodedirtylist) { while (vnode_dirty_list) {
// in the case of failure, only the first error to occur will be reported and we will not // in the case of failure, only the first error to occur will be reported and we will not
// retry the write and keep on syncing the pages to disk // retry the write and keep on syncing the pages to disk
HOLD_LOCK(); HOLD_LOCK();
page_t *page = vnodedirtylist; page_t *page = vnode_dirty_list;
vnodedirtylist = vnodedirtylist->write_next; vnode_dirty_list = vnode_dirty_list->write_next;
page->write_next = NULL; page->write_next = NULL;
page->flags &= ~PAGE_FLAGS_VNODE_SYNCING; page->flags &= ~PAGE_FLAGS_VNODE_SYNCING;
// another thread could already have synced this page, verify if it is still dirty // another thread could already have synced this page, verify if it is still dirty
if (page->flags & PAGE_FLAGS_DIRTY) { if (page->flags & PAGE_FLAGS_DIRTY) {
int error = syncpage(page, false); int error = sync_page(page, false);
if (e == 0) if (e == 0)
e = error; e = error;
} else { } else {
RELEASE_LOCK(); RELEASE_LOCK();
} }
// syncpage returns with lock released // sync_page returns with lock released
} }
return e; return e;
} }
int mm_cache_sync(void) { int mm_cache_sync(void) {
eventlistener_t eventlistener; eventlistener_t event_listener;
EVENT_INITLISTENER(&eventlistener); EVENT_INITLISTENER(&event_listener);
HOLD_LOCK(); HOLD_LOCK();
if (dirtylist == NULL) { if (dirty_list == NULL) {
// no dirty pages // no dirty pages
RELEASE_LOCK(); RELEASE_LOCK();
return 0; return 0;
} }
EVENT_ATTACH(&eventlistener, &syncevent); EVENT_ATTACH(&event_listener, &sync_event);
semaphore_signal(&sync); semaphore_signal(&sync);
RELEASE_LOCK(); RELEASE_LOCK();
EVENT_WAIT(&eventlistener, 0); EVENT_WAIT(&event_listener, 0);
EVENT_DETACHALL(&eventlistener); EVENT_DETACHALL(&event_listener);
return 0; return 0;
} }
// backing expected locked // backing expected locked
int mm_cache_make_dirty(page_t *page) { int mm_cache_make_dirty(page_t *page) {
bool madedirty = false; bool made_dirty = false;
HOLD_LOCK(); HOLD_LOCK();
if ((page->flags & (PAGE_FLAGS_DIRTY | PAGE_FLAGS_TRUNCATED)) == 0) { if ((page->flags & (PAGE_FLAGS_DIRTY | PAGE_FLAGS_TRUNCATED)) == 0) {
madedirty = true; made_dirty = true;
// page is neither dirty nor truncated, add to dirty list and hold the page and vnode // page is neither dirty nor truncated, add to dirty list and hold the page and vnode
page->flags |= PAGE_FLAGS_DIRTY; page->flags |= PAGE_FLAGS_DIRTY;
if ((page->flags & PAGE_FLAGS_VNODE_SYNCING) == 0) { if ((page->flags & PAGE_FLAGS_VNODE_SYNCING) == 0) {
page->write_prev = NULL; page->write_prev = NULL;
page->write_next = dirtylist; page->write_next = dirty_list;
if (dirtylist) if (dirty_list)
dirtylist->write_prev = page; dirty_list->write_prev = page;
else else
dirtylistend = page; dirty_list_end = page;
dirtylist = page; dirty_list = page;
} }
mm_hold_page(mm_get_page_address(page)); mm_hold_page(mm_get_page_address(page));
@@ -433,7 +445,7 @@ int mm_cache_make_dirty(page_t *page) {
} }
RELEASE_LOCK(); RELEASE_LOCK();
if (madedirty) { if (made_dirty) {
vattr_t attr; vattr_t attr;
attr.mtime = timekeeper_time(); attr.mtime = timekeeper_time();
VOP_SETATTR(page->backing, &attr, V_ATTR_MTIME, NULL); VOP_SETATTR(page->backing, &attr, V_ATTR_MTIME, NULL);
@@ -446,30 +458,30 @@ static void tick(context_t *, dpcarg_t arg) {
} }
static void writer() { static void writer() {
timerentry_t timerentry; timerentry_t timer_entry;
// this will be inserted on some random cpu's timer, but it will always work after that // this will be inserted on some random cpu's timer, but it will always work after that
interrupt_set(false); interrupt_set(false);
timer_insert(current_cpu()->timer, &timerentry, tick, NULL, (uintmax_t)WRITER_TICK_SECONDS * 1000000, true); timer_insert(current_cpu()->timer, &timer_entry, tick, NULL, (uintmax_t)WRITER_TICK_SECONDS * 1000000, true);
interrupt_set(true); interrupt_set(true);
for (;;) { for (;;) {
HOLD_LOCK(); HOLD_LOCK();
volatile page_t *page = dirtylistend; volatile page_t *page = dirty_list_end;
if (page == NULL) { if (page == NULL) {
EVENT_SIGNAL(&syncevent); EVENT_SIGNAL(&sync_event);
RELEASE_LOCK(); RELEASE_LOCK();
semaphore_wait(&sync, false); semaphore_wait(&sync, false);
continue; continue;
} }
dirtylistend = page->write_prev; dirty_list_end = page->write_prev;
if (dirtylistend) if (dirty_list_end)
dirtylistend->write_next = NULL; dirty_list_end->write_next = NULL;
else else
dirtylist = NULL; dirty_list = NULL;
page->write_prev = NULL; page->write_prev = NULL;
// TODO notify error on mm_cache_sync_vnode // TODO notify error on mm_cache_sync_vnode
syncpage((page_t *)page, true); sync_page((page_t *)page, true);
} }
} }
@@ -480,12 +492,12 @@ void mm_cache_init(void) {
memset(table, 0, TABLE_SIZE * sizeof(page_t *)); memset(table, 0, TABLE_SIZE * sizeof(page_t *));
SEMAPHORE_INIT(&sync, 0); SEMAPHORE_INIT(&sync, 0);
writerthread = sched_newthread(writer, PAGE_SIZE * 16, 1, NULL, NULL); writer_thread = sched_newthread(writer, PAGE_SIZE * 16, 1, NULL, NULL);
__assert(writerthread); __assert(writer_thread);
sched_queue(writerthread); sched_queue(writer_thread);
mm_cache_sync(); mm_cache_sync();
EVENT_INITHEADER(&syncevent); EVENT_INITHEADER(&sync_event);
EVENT_INITHEADER(&pagereadyevent); EVENT_INITHEADER(&page_ready_event);
} }
INIT_ROUTINE_DEFINE(mm_cache, INIT_ROUTINE_FLAGS_NONE, mm_cache_init, scheduler); INIT_ROUTINE_DEFINE(mm_cache, INIT_ROUTINE_FLAGS_NONE, mm_cache_init, scheduler);