Second Extended Filesystem (ext2) and other improvements (#33)
[lunaix-os.git] / lunaix-os / kernel / fs / pcache.c
index 3db75718592896c96b0968c73c70b84dd2760240..4a280db1849b7d8af5b9c131c1f462d74958f45e 100644 (file)
@@ -2,28 +2,87 @@
 #include <lunaix/ds/btrie.h>
 #include <lunaix/fs.h>
 #include <lunaix/mm/page.h>
-#include <lunaix/mm/pmm.h>
 #include <lunaix/mm/valloc.h>
-#include <lunaix/mm/vmm.h>
 #include <lunaix/spike.h>
+#include <lunaix/bcache.h>
+#include <lunaix/syslog.h>
 
-#define PCACHE_DIRTY 0x1
+LOG_MODULE("pcache")
+
+#define pcache_obj(bcache) container_of(bcache, struct pcache, cache)
+
+void pcache_release_page(struct pcache* pcache, struct pcache_pg* page);
+void pcache_set_dirty(struct pcache* pcache, struct pcache_pg* pg);
+
+static bcache_zone_t pagecached_zone = NULL;
+
+static void
+__pcache_sync(struct bcache* bc, unsigned long tag, void* data)
+{
+    struct pcache* cache;
+
+    cache = pcache_obj(bc);
+    pcache_commit(cache->master, (struct pcache_pg*)data);
+}
+
+static void
+__pcache_try_release(struct bcache* bc, void* data)
+{
+    struct pcache_pg* page;
+    
+    page = (struct pcache_pg*)data;
+    pcache_release_page(pcache_obj(bc), page);
+}
+
+static struct bcache_ops cache_ops = {
+    .release_on_evict = __pcache_try_release,
+    .sync_cached = __pcache_sync
+};
+
+static void*
+pcache_alloc_page()
+{
+    int i = 0;
+    ptr_t va = 0;
+    struct leaflet* leaflet = alloc_leaflet(0);
+
+    if (!leaflet) {
+        return NULL;
+    }
+
+    if (!(va = (ptr_t)vmap(leaflet, KERNEL_DATA))) {
+        leaflet_return(leaflet);
+        return NULL;
+    }
+
+    return (void*)va;
+}
+
+static void
+pcache_free_page(void* va)
+{
+    pte_t* ptep = mkptep_va(VMS_SELF, (ptr_t)va);
+    pte_t pte = pte_at(ptep);
+    leaflet_return(pte_leaflet(pte));
+}
 
 void
 pcache_init(struct pcache* pcache)
 {
-    btrie_init(&pcache->tree, PG_SIZE_BITS);
+    if (unlikely(!pagecached_zone)) {
+        pagecached_zone = bcache_create_zone("pcache");
+    }
+
     llist_init_head(&pcache->dirty);
-    llist_init_head(&pcache->pages);
+
+    bcache_init_zone(&pcache->cache, pagecached_zone, 4, -1, 
+                     sizeof(struct pcache_pg), &cache_ops);
 }
 
 void
 pcache_release_page(struct pcache* pcache, struct pcache_pg* page)
 {
-    pmm_free_page(KERNEL_PID, page->pg);
-    vmm_del_mapping(PD_REFERENCED, page->pg);
-
-    llist_delete(&page->pg_list);
+    pcache_free_page(page->data);
 
     vfree(page);
 
@@ -31,15 +90,18 @@ pcache_release_page(struct pcache* pcache, struct pcache_pg* page)
 }
 
 struct pcache_pg*
-pcache_new_page(struct pcache* pcache, uint32_t index)
+pcache_new_page(struct pcache* pcache)
 {
-    void* pg = pmm_alloc_page(KERNEL_PID, 0);
-    void* pg_v = vmm_vmap(pg, PG_SIZE, PG_PREM_URW);
-    struct pcache_pg* ppg = vzalloc(sizeof(struct pcache_pg));
-    ppg->pg = pg_v;
+    struct pcache_pg* ppg;
+    void* data_page;
+    
+    data_page = pcache_alloc_page();
+    if (!data_page) {
+        return NULL;
+    }
 
-    llist_append(&pcache->pages, &ppg->pg_list);
-    btrie_set(&pcache->tree, index, ppg);
+    ppg = vzalloc(sizeof(struct pcache_pg));
+    ppg->data = data_page;
 
     return ppg;
 }
@@ -47,128 +109,174 @@ pcache_new_page(struct pcache* pcache, uint32_t index)
 void
 pcache_set_dirty(struct pcache* pcache, struct pcache_pg* pg)
 {
-    if (!(pg->flags & PCACHE_DIRTY)) {
-        pg->flags |= PCACHE_DIRTY;
-        pcache->n_dirty++;
-        llist_append(&pcache->dirty, &pg->dirty_list);
+    if (pg->dirty) {
+        return;
     }
+
+    pg->dirty = true;
+    pcache->n_dirty++;
+    llist_append(&pcache->dirty, &pg->dirty_list);
 }
 
-struct pcache_pg*
-pcache_get_page(struct pcache* pcache,
-                uint32_t index,
-                uint32_t* offset,
-                struct pcache_pg** page)
+static bcobj_t
+__getpage_and_lock(struct pcache* pcache, unsigned int tag, 
+                   struct pcache_pg** page)
 {
-    struct pcache_pg* pg = btrie_get(&pcache->tree, index);
-    int is_new = 0;
-    *offset = index & ((1 << pcache->tree.truncated) - 1);
-    if (!pg) {
-        pg = pcache_new_page(pcache, index);
-        pg->fpos = index - *offset;
-        pcache->n_pages++;
-        is_new = 1;
+    bcobj_t cobj;
+    struct pcache_pg* pg;
+
+    if (bcache_tryget(&pcache->cache, tag, &cobj))
+    {
+        *page = (struct pcache_pg*)bcached_data(cobj);
+        return cobj;
+    }
+
+    pg = pcache_new_page(pcache);
+    if (pg) {
+        pg->index = tag;
     }
+
     *page = pg;
-    return is_new;
+
+    return NULL;
+}
+
+static inline int
+__fill_page(struct v_inode* inode, struct pcache_pg* pg, unsigned int index)
+{
+    return inode->default_fops->read_page(inode, pg->data, page_addr(index));
 }
 
 int
-pcache_write(struct v_file* file, void* data, uint32_t len, uint32_t fpos)
+pcache_write(struct v_inode* inode, void* data, u32_t len, u32_t fpos)
 {
-    uint32_t pg_off, buf_off = 0;
-    struct pcache* pcache = file->inode->pg_cache;
+    int errno = 0;
+    unsigned int tag, off, wr_cnt;
+    unsigned int end = fpos + len;
+    struct pcache* pcache;
     struct pcache_pg* pg;
+    bcobj_t obj;
+
+    pcache = inode->pg_cache;
+    
+    while (fpos < end && errno >= 0) {
+        tag = pfn(fpos);
+        off = va_offset(fpos);
+        wr_cnt = MIN(end - fpos, PAGE_SIZE - off);
 
-    while (buf_off < len) {
-        pcache_get_page(pcache, fpos, &pg_off, &pg);
-        uint32_t wr_bytes = MIN(PG_SIZE - pg_off, len - buf_off);
-        memcpy(pg->pg + pg_off, (data + buf_off), wr_bytes);
+        obj = __getpage_and_lock(pcache, tag, &pg);
+
+        if (!obj && !pg) {
+            errno = inode->default_fops->write(inode, data, fpos, wr_cnt);
+            goto cont;
+        }
 
+        // new page and unaligned write, then prepare for partial override
+        if (!obj && wr_cnt != PAGE_SIZE) {
+            errno = __fill_page(inode, pg, tag);
+            if (errno < 0) {
+                return errno;
+            }
+        }
+        
+        memcpy(offset(pg->data, off), data, wr_cnt);
         pcache_set_dirty(pcache, pg);
 
-        buf_off += wr_bytes;
-        fpos += wr_bytes;
+        if (obj) {
+            bcache_return(obj);
+        } else {
+            bcache_put(&pcache->cache, tag, pg);
+        }
+
+cont:
+        data  = offset(data, wr_cnt);
+        fpos += wr_cnt;
     }
 
-    return buf_off;
+    return errno < 0 ? errno : (int)(len - (end - fpos));
 }
 
 int
-pcache_read(struct v_file* file, void* data, uint32_t len, uint32_t fpos)
+pcache_read(struct v_inode* inode, void* data, u32_t len, u32_t fpos)
 {
-    uint32_t pg_off, buf_off = 0, new_pg = 0;
     int errno = 0;
-    struct pcache* pcache = file->inode->pg_cache;
+    unsigned int tag, off, rd_cnt;
+    unsigned int end = fpos + len, size = 0;
+    struct pcache* pcache;
     struct pcache_pg* pg;
-    struct v_inode* inode = file->inode;
-
-    while (buf_off < len) {
-        if (pcache_get_page(pcache, fpos, &pg_off, &pg)) {
-            // Filling up the page
-            errno = inode->default_fops.read(file, pg->pg, PG_SIZE, pg->fpos);
-            if (errno >= 0 && errno < PG_SIZE) {
-                // EOF
-                len = buf_off + errno;
-            } else if (errno < 0) {
-                break;
+    bcobj_t obj;
+
+    pcache = inode->pg_cache;
+
+    while (fpos < page_upaligned(end)) {
+        tag = pfn(fpos);
+        off = va_offset(fpos);
+
+        obj = __getpage_and_lock(pcache, tag, &pg);
+
+        if (!obj) {
+            errno = __fill_page(inode, pg, tag);
+            if (errno < 0) {
+                return errno;
             }
+
+            end -= (PAGE_SIZE - errno);
         }
-        uint32_t rd_bytes = MIN(PG_SIZE - pg_off, len - buf_off);
-        memcpy((data + buf_off), pg->pg + pg_off, rd_bytes);
 
-        buf_off += rd_bytes;
-        fpos += rd_bytes;
+        rd_cnt = MIN(end - fpos, PAGE_SIZE - off);
+        memcpy(data, pg->data + off, rd_cnt);
+
+        if (obj) {
+            bcache_return(obj);
+        } else {
+            bcache_put(&pcache->cache, tag, pg);
+        }
+
+        data += rd_cnt;
+        size += rd_cnt;
+        fpos = page_aligned(fpos + PAGE_SIZE);
     }
 
-    return errno < 0 ? errno : buf_off;
+    return errno < 0 ? errno : (int)size;
 }
 
 void
 pcache_release(struct pcache* pcache)
 {
-    struct pcache_pg *pos, *n;
-    llist_for_each(pos, n, &pcache->pages, pg_list)
-    {
-        vfree(pos);
-    }
-
-    btrie_release(&pcache->tree);
+    bcache_free(&pcache->cache);
 }
 
 int
-pcache_commit(struct v_file* file, struct pcache_pg* page)
+pcache_commit(struct v_inode* inode, struct pcache_pg* page)
 {
-    if (!(page->flags & PCACHE_DIRTY)) {
-        return;
+    if (!page->dirty) {
+        return 0;
     }
 
-    struct v_inode* inode = file->inode;
-    int errno = inode->default_fops.write(file, page->pg, PG_SIZE, page->fpos);
-
+    int errno;
+    unsigned int fpos = page_addr(page->index);
+    
+    errno = inode->default_fops->write_page(inode, page->data, fpos);
     if (!errno) {
-        page->flags &= ~PCACHE_DIRTY;
+        page->dirty = false;
         llist_delete(&page->dirty_list);
-        file->inode->pg_cache->n_dirty--;
+        inode->pg_cache->n_dirty--;
     }
 
     return errno;
 }
 
 void
-pcache_commit_all(struct v_file* file)
+pcache_commit_all(struct v_inode* inode)
 {
-    struct pcache* cache = file->inode->pg_cache;
+    struct pcache* cache = inode->pg_cache;
+    if (!cache) {
+        return;
+    }
+
     struct pcache_pg *pos, *n;
     llist_for_each(pos, n, &cache->dirty, dirty_list)
     {
-        pcache_commit(file, pos);
+        pcache_commit(inode, pos);
     }
-}
-
-void
-pcache_invalidate(struct v_file* file, struct pcache_pg* page)
-{
-    pcache_commit(file, page);
-    pcache_release_page(&file->inode->pg_cache, page);
 }
\ No newline at end of file