page cache rewrite

This commit is contained in:
Matheus
2026-07-28 16:33:55 -03:00
parent ad1740738b
commit 31f9ae17cf
17 changed files with 494 additions and 608 deletions
+1 -1
View File
@@ -301,4 +301,4 @@ static void abc_init_routine(void) {
__assert(abc_block_cache);
}
INIT_ROUTINE_DEFINE(abc, INIT_ROUTINE_FLAGS_NONE, abc_init_routine, scheduler, trie);
INIT_ROUTINE_DEFINE(abc, INIT_ROUTINE_FLAGS_NONE, abc_init_routine, mm_cache, scheduler, trie);
+4 -21
View File
@@ -1822,7 +1822,6 @@ static int ext2_getpage(vnode_t *node, uintmax_t offset, struct page_t *page) {
static int ext2_putpage(vnode_t *node, uintmax_t offset, struct page_t *page) {
// only regular files get cached
__assert(node->type == V_TYPE_REGULAR);
size_t writec;
iovec_t iovec = {
.addr = MAKE_HHDM(mm_get_page_address(page)),
.len = PAGE_SIZE
@@ -1830,21 +1829,8 @@ static int ext2_putpage(vnode_t *node, uintmax_t offset, struct page_t *page) {
iovec_iterator_t iovec_iterator;
iovec_iterator_init(&iovec_iterator, &iovec, 1);
int error = VOP_WRITE(node, &iovec_iterator, PAGE_SIZE, offset, 0, &writec, NULL);
// its possible that writec is zero here. the condition where this is possible is when
// the file is truncated after the check in the page sync function but before it actually is
// written back to disk. therefore, we check that the page IS infact truncated if this happens.
// if it isn't, something else happened and its not safe to continue.
//
// TODO: since this check will likely be copied between different filesystem drivers, it could be interesting to have
// ext2_putpage take in a (size_t *) pointer and have this check be in whatever function calls VOP_PUTPAGE.
if (writec == 0) {
__assert(page->flags & PAGE_FLAGS_TRUNCATED);
}
return error;
size_t writec;
return VOP_WRITE(node, &iovec_iterator, PAGE_SIZE, offset, 0, &writec, NULL);
}
static int ext2_rename(vnode_t *sourcedir, vnode_t *source, char *oldname, vnode_t *targetdir, char *newname, int flags) {
@@ -1964,13 +1950,11 @@ static int ext2_rename(vnode_t *sourcedir, vnode_t *source, char *oldname, vnode
}
static int ext2_sync(vnode_t *vnode) {
int e = mm_cache_sync_vnode(vnode, 0, UINT64_MAX);
int e = mm_cache_sync_vnode(vnode);
ext2fs_t *fs = (ext2fs_t *)vnode->vfs;
abc_sync(&fs->abc);
// TODO don't sync the entire disk but rather only the inodes and blocks
VOP_LOCK(fs->backing);
int e2 = mm_cache_sync_vnode(fs->backing, 0, UINT64_MAX);
VOP_UNLOCK(fs->backing);
int e2 = mm_cache_sync_vnode(fs->backing);
// only the first errors are reported
return e ? e : e2;
}
@@ -2007,7 +1991,6 @@ static int ext2_inactive(vnode_t *vnode) {
__assert(hashtable_remove(&fs->inodetable, &node->id, sizeof(node->id)) == 0);
MUTEX_RELEASE(&fs->inodetablelock);
mm_cache_truncate(vnode, 0);
freeinode(fs, &node->inode, node->id);
slab_free(nodecache, node);
+7 -26
View File
@@ -200,9 +200,7 @@ static int fatfs_unmount(vfs_t *vfs) {
static int fatfs_sync(vfs_t *vfs) {
fatfs_t *fatfs = (fatfs_t *)vfs;
abc_sync(&fatfs->abc);
VOP_LOCK(fatfs->backing);
int error = mm_cache_sync_vnode(fatfs->backing, 0, UINT64_MAX);
VOP_UNLOCK(fatfs->backing);
int error = mm_cache_sync_vnode(fatfs->backing);
return error;
}
@@ -264,19 +262,16 @@ static int fatfs_syncvnode(vnode_t *vnode) {
fatnode_t *fatnode = (fatnode_t *)vnode;
// sync file data
int e = mm_cache_sync_vnode(vnode, 0, UINT64_MAX);
int e = mm_cache_sync_vnode(vnode);
abc_sync(&fs->abc);
VOP_LOCK(fs->backing);
// sync the dent
int e2 = 0;
if (fatnode->dent_disk_offset)
e2 = mm_cache_sync_vnode(fs->backing, fatnode->dent_disk_offset, sizeof(fatfs_dent_t));
e2 = mm_cache_sync_vnode(fs->backing);
// and sync the fats
int e3 = mm_cache_sync_vnode(fs->backing, fs->fat_offset, fs->fat_size * fs->fat_count);
VOP_UNLOCK(fs->backing);
int e3 = mm_cache_sync_vnode(fs->backing);
// only the first errors are reported
if (e)
@@ -391,7 +386,6 @@ static int fatfs_read(vnode_t *vnode, iovec_iterator_t *iovec_iterator, size_t s
static int fatfs_putpage(vnode_t *node, uintmax_t offset, struct page_t *page) {
// only regular files get cached
__assert(node->type == V_TYPE_REGULAR);
size_t write_count;
iovec_t iovec = {
.addr = MAKE_HHDM(mm_get_page_address(page)),
.len = PAGE_SIZE
@@ -399,23 +393,11 @@ static int fatfs_putpage(vnode_t *node, uintmax_t offset, struct page_t *page) {
iovec_iterator_t iovec_iterator;
iovec_iterator_init(&iovec_iterator, &iovec, 1);
int error = VOP_WRITE(node, &iovec_iterator, PAGE_SIZE, offset, 0, &write_count, NULL);
// its possible that write_count is zero here. the condition where this is possible is when
// the file is truncated after the check in the page sync function but before it actually is
// written back to disk. therefore, we check that the page IS infact truncated if this happens.
// if it isn't, something else happened and its not safe to continue.
//
// TODO: since this check will likely be copied between different filesystem drivers, it could be interesting to have
// fatfs_putpage take in a (size_t *) pointer and have this check be in whatever function calls VOP_PUTPAGE.
if (write_count == 0) {
__assert(page->flags & PAGE_FLAGS_TRUNCATED);
}
return error;
size_t write_count;
return VOP_WRITE(node, &iovec_iterator, PAGE_SIZE, offset, 0, &write_count, NULL);
}
static int fatfs_getpage(vnode_t *node, uintmax_t offset, struct page_t *page) {
// only regular files get cached
__assert(node->type == V_TYPE_REGULAR);
@@ -970,7 +952,6 @@ static int fatfs_inactive(vnode_t *vnode) {
__assert(!"fatfs VOP_INACTIVE() with hardlink count >0 is currently unimplemented");
} else {
// unlinked file having resources released when kernel refcount hits zero
mm_cache_truncate(vnode, 0);
fatfs_resize_file(fatfs, fatnode, 0);
}
+42 -6
View File
@@ -224,6 +224,36 @@ static int tmpfs_root(vfs_t *vfs, vnode_t **vnode) {
return 0;
}
static int tmpfs_rw(vnode_t *node, iovec_iterator_t *iovec_iterator, size_t size, uintmax_t offset, size_t *done, bool write) {
*done = 0;
while (*done < size) {
uintmax_t current_offset = offset + *done;
uintmax_t page_offset = current_offset / PAGE_SIZE * PAGE_SIZE;
size_t page_start = current_offset % PAGE_SIZE;
size_t copy_size = min(PAGE_SIZE - page_start, size - *done);
page_t *page;
int error = mm_cache_get_page(node, page_offset, 0, &page);
if (error)
return error;
void *address = MAKE_HHDM(mm_get_page_address(page));
if (write)
error = iovec_iterator_copy_to_buffer(iovec_iterator, (void *)((uintptr_t)address + page_start), copy_size);
else
error = iovec_iterator_copy_from_buffer(iovec_iterator, (void *)((uintptr_t)address + page_start), copy_size);
mm_release_page(FROM_HHDM(address));
if (error)
return error;
*done += copy_size;
}
if (write && *done)
((tmpfsnode_t *)node)->attr.mtime = timekeeper_time();
return 0;
}
static int tmpfs_read(vnode_t *node, iovec_iterator_t *iovec_iterator, size_t size, uintmax_t offset, int flags, size_t *readc, cred_t *cred) {
if (node->type == V_TYPE_DIR)
return EISDIR;
@@ -231,7 +261,13 @@ static int tmpfs_read(vnode_t *node, iovec_iterator_t *iovec_iterator, size_t si
if (node->type != V_TYPE_REGULAR)
return EINVAL;
__assert(!"tmpfs_read is handled by the page cache");
tmpfsnode_t *tmpfsnode = (tmpfsnode_t *)node;
if (offset >= tmpfsnode->attr.size) {
*readc = 0;
return 0;
}
size = min(size, tmpfsnode->attr.size - offset);
return tmpfs_rw(node, iovec_iterator, size, offset, readc, false);
}
static int tmpfs_write(vnode_t *node, iovec_iterator_t *iovec_iterator, size_t size, uintmax_t offset, int flags, size_t *writec, cred_t *cred) {
@@ -241,7 +277,10 @@ static int tmpfs_write(vnode_t *node, iovec_iterator_t *iovec_iterator, size_t s
if (node->type != V_TYPE_REGULAR)
return EINVAL;
__assert(!"tmpfs_read is handled by the page cache");
if (size + offset < offset)
return EOVERFLOW;
__assert(size + offset <= ((tmpfsnode_t *)node)->attr.size);
return tmpfs_rw(node, iovec_iterator, size, offset, writec, true);
}
static int tmpfs_access(vnode_t *vnode, mode_t mode, cred_t *cred) {
@@ -501,9 +540,6 @@ static int tmpfs_inactive(vnode_t *node) {
arch_e9_puts("tmpfs_inactive\n");
tmpfsnode_t *tmp_node = (tmpfsnode_t *)node;
__assert(tmp_node->attr.nlinks == (node->type == V_TYPE_DIR ? 1 : 0));
if (node->type == V_TYPE_REGULAR) {
mm_cache_truncate(node, 0);
}
freenode(tmp_node);
return 0;
}
@@ -535,7 +571,7 @@ static int tmpfs_getpage(vnode_t *node, uintmax_t offset, struct page_t *page) {
void *phyhhdm = MAKE_HHDM(phy);
mm_hold_page(phy);
page->flags |= PAGE_FLAGS_PINNED;
__atomic_fetch_or(&page->flags, PAGE_FLAGS_PINNED, __ATOMIC_RELAXED);
memset(phyhhdm, 0, PAGE_SIZE);
return 0;
}
+24 -48
View File
@@ -152,6 +152,9 @@ int vfs_sync(void) {
}
void vfs_inactive(vnode_t *vnode) {
if (vfs_iscacheable(vnode))
mm_cache_truncate(vnode, 0);
if (vnode->type == V_TYPE_SOCKET && vnode->socketbinding) {
localsock_leavebinding(vnode);
} else if (vnode->type == V_TYPE_FIFO && vnode->fifobinding) {
@@ -438,33 +441,14 @@ static void bytestopages(uintmax_t offset, size_t size, uintmax_t *pageoffset, s
*pagecount = toppage - *pageoffset;
}
static int writenocache(vnode_t *node, page_t *page, uintmax_t pageoffset) {
// don't keep the pages in the cache!
// if its a file, do the proper filesystem sync.
// if its a block device, sync only the page we just dirtied
VOP_LOCK(node);
int e;
if (node->type == V_TYPE_REGULAR)
e = VOP_SYNC(node);
else
e = mm_cache_sync_vnode(node, pageoffset, PAGE_SIZE);
VOP_UNLOCK(node);
// try to turn it into anonymous memory
mm_cache_evict(page);
return e;
}
int vfs_write_iovec(vnode_t *node, iovec_iterator_t *iovec_iterator, size_t size, uintmax_t offset, size_t *written, int flags) {
int err = 0;
if (vfs_iscacheable(node)) {
bool cacheable = vfs_iscacheable(node);
if (cacheable) {
*written = 0;
// can't write a size 0 buffer
if (size == 0)
return 0;
// overflow
if (size + offset < offset)
return EINVAL;
@@ -502,7 +486,9 @@ int vfs_write_iovec(vnode_t *node, iovec_iterator_t *iovec_iterator, size_t size
size = min(size + offset, bytesize) - offset;
}
}
if (cacheable && (flags & V_FFLAGS_NOCACHE) == 0) {
uintmax_t pageoffset, pagecount, startoffset;
bytestopages(offset, size, &pageoffset, &pagecount, &startoffset);
page_t *page = NULL;
@@ -525,15 +511,10 @@ int vfs_write_iovec(vnode_t *node, iovec_iterator_t *iovec_iterator, size_t size
mm_cache_make_dirty(page);
*written += writesize;
if (flags & V_FFLAGS_NOCACHE)
err = writenocache(node, page, pageoffset * PAGE_SIZE);
pageoffset += 1;
pagecount -= 1;
mm_release_page(FROM_HHDM(address));
if (err)
goto leave;
}
for (uintmax_t offset = 0; offset < pagecount * PAGE_SIZE; offset += PAGE_SIZE) {
@@ -554,30 +535,30 @@ int vfs_write_iovec(vnode_t *node, iovec_iterator_t *iovec_iterator, size_t size
mm_cache_make_dirty(page);
*written += writesize;
if (flags & V_FFLAGS_NOCACHE)
err = writenocache(node, page, pageoffset * PAGE_SIZE + offset);
mm_release_page(FROM_HHDM(address));
if (err)
goto leave;
}
leave:
MUTEX_RELEASE(&node->size_lock);
if (flags & V_FFLAG_MUST_SYNC)
err = mm_cache_sync_vnode(node);
} else {
// special file, just write as its not being cached
// Non-cacheable objects and direct I/O bypass the page cache.
VOP_LOCK(node);
err = VOP_WRITE(node, iovec_iterator, size, offset, flags, written, getcred());
VOP_UNLOCK(node);
}
leave:
if (cacheable)
MUTEX_RELEASE(&node->size_lock);
return err;
}
int vfs_read_iovec(vnode_t *node, iovec_iterator_t *iovec_iterator, size_t size, uintmax_t offset, size_t *bytesread, int flags) {
int err = 0;
if (vfs_iscacheable(node)) {
bool cacheable = vfs_iscacheable(node);
if (cacheable) {
*bytesread = 0;
// can't read 0 bytes from the cache
if (size == 0)
@@ -615,7 +596,9 @@ int vfs_read_iovec(vnode_t *node, iovec_iterator_t *iovec_iterator, size_t size,
goto leave;
size = min(size + offset, nodesize) - offset;
}
if (cacheable && (flags & V_FFLAGS_NOCACHE) == 0) {
uintmax_t pageoffset, pagecount, startoffset;
bytestopages(offset, size, &pageoffset, &pagecount, &startoffset);
page_t *page = NULL;
@@ -637,11 +620,6 @@ int vfs_read_iovec(vnode_t *node, iovec_iterator_t *iovec_iterator, size_t size,
*bytesread += readsize;
if (flags & V_FFLAGS_NOCACHE) {
// try to turn it into anonymous memory
mm_cache_evict(page);
}
pageoffset += 1;
pagecount -= 1;
@@ -664,20 +642,18 @@ int vfs_read_iovec(vnode_t *node, iovec_iterator_t *iovec_iterator, size_t size,
}
*bytesread += readsize;
if (flags & V_FFLAGS_NOCACHE) {
// try to turn it into anonymous memory
mm_cache_evict(page);
}
mm_release_page(FROM_HHDM(address));
}
leave:
MUTEX_RELEASE(&node->size_lock);
} else {
// special file, just read as size doesn't matter
// Non-cacheable objects and direct I/O bypass the page cache.
VOP_LOCK(node);
err = VOP_READ(node, iovec_iterator, size, offset, flags, bytesread, getcred());
VOP_UNLOCK(node);
}
leave:
if (cacheable)
MUTEX_RELEASE(&node->size_lock);
return err;
}