aboutsummaryrefslogtreecommitdiff
path: root/src/kernel/user/pager.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/kernel/user/pager.c')
-rw-r--r--src/kernel/user/pager.c90
1 files changed, 66 insertions, 24 deletions
diff --git a/src/kernel/user/pager.c b/src/kernel/user/pager.c
index 45d2970..9987813 100644
--- a/src/kernel/user/pager.c
+++ b/src/kernel/user/pager.c
@@ -89,25 +89,7 @@ bool swap_pager_resize(pager_t *p, size_t new_size) {
swap_page_release(p, PAGE_ALIGN_UP(new_size), p->size - PAGE_ALIGN_UP(new_size));
- size_t last_page = PAGE_ALIGN_DOWN(new_size);
-
- if (!PAGE_ALIGNED(new_size) && hashtbl_find(p->pages, (void*)last_page) != 0) {
- void *region = region_alloc(PAGE_SIZE, "Page zeroing area");
- if (!region) PANIC("TODO");
-
- uint32_t frame = (uint32_t)hashtbl_find(p->pages, (void*)last_page) >> ENT_FRAME_SHIFT;
- ASSERT(frame != 0);
-
- if (!pd_map_page(region, frame, true)) PANIC("TODO");
-
- size_t b0 = new_size - last_page;
- memset(region + b0, 0, PAGE_SIZE - b0);
-
- pd_unmap_page(region);
-
- region_free(region);
- }
-
+ p->size = new_size;
return true;
}
@@ -187,20 +169,80 @@ void vfs_page_in(pager_t *p, size_t offset, size_t len) {
}
void vfs_page_commit(pager_t *p, size_t offset, size_t len) {
- // TODO
+ ASSERT(PAGE_ALIGNED(offset));
+ ASSERT(p->vfs_pager.ops->read != 0);
+
+ void *region = region_alloc(PAGE_SIZE, "Page saving area");
+ if (region == 0) return;
+
+ for (size_t page = offset; page < offset + len; page += PAGE_SIZE) {
+ uint32_t ent = (uint32_t)hashtbl_find(p->pages, (void*)page);
+ if (ent == 0) continue;
+
+ if (ent & PAGE_DIRTY) {
+ uint32_t frame = ent >> ENT_FRAME_SHIFT;
+
+ size_t expect_len = PAGE_SIZE;
+ if (page + expect_len > p->size) {
+ expect_len = p->size - page;
+ }
+
+ if (!pd_map_page(region, frame, true)) continue;
+
+ if (p->vfs_pager.ops->write) {
+ size_t write_len = p->vfs_pager.ops->write(p->vfs_pager.node, page, expect_len, (char*)region);
+ if (write_len == expect_len) {
+ hashtbl_change(p->pages, (void*)page, (void*)(ent & ~PAGE_DIRTY));
+ }
+ } else {
+ dbg_printf("Warning: read-only page was dirtied! Changes will be lost.\n");
+ }
+
+ pd_unmap_page(region);
+ }
+ }
+
+ region_free(region);
}
void vfs_page_out(pager_t *p, size_t offset, size_t len) {
- // TODO
+ vfs_page_commit(p, offset, len);
+
+ for (size_t page = offset; page < offset + len; page += PAGE_SIZE) {
+ uint32_t ent = (uint32_t)hashtbl_find(p->pages, (void*)page);
+ if (ent != 0) {
+ if (ent & PAGE_DIRTY) continue; // for some reason page could not be commited ; keep it for nwo
+
+ hashtbl_remove(p->pages, (void*)page);
+ frame_free(ent >> ENT_FRAME_SHIFT, 1);
+ }
+ }
}
void vfs_page_release(pager_t *p, size_t offset, size_t len) {
- // TODO
+ ASSERT(PAGE_ALIGNED(offset));
+ ASSERT(p->vfs_pager.ops->read != 0);
+
+ for (size_t page = offset; page < offset + len; page += PAGE_SIZE) {
+ uint32_t ent = (uint32_t)hashtbl_find(p->pages, (void*)page);
+ if (ent != 0) {
+ if (ent & PTE_DIRTY) {
+ dbg_printf("Warning: releasing a dirtied VFS page (it probably could not be written earlier) ; data will be lost.\n");
+ }
+ hashtbl_remove(p->pages, (void*)page);
+ frame_free(ent >> ENT_FRAME_SHIFT, 1);
+ }
+ }
}
bool vfs_pager_resize(pager_t *p, size_t new_size) {
- // TODO
- return false;
+ if (p->vfs_pager.ops->resize == 0) return false;
+ if (!p->vfs_pager.ops->resize(p->vfs_pager.node, new_size)) return false;
+
+ vfs_page_release(p, PAGE_ALIGN_UP(new_size), p->size - PAGE_ALIGN_UP(new_size));
+
+ p->size = new_size;
+ return true;
}
// ============ //