1 #include <klibc/string.h>
2 #include <lunaix/ds/btrie.h>
4 #include <lunaix/mm/page.h>
5 #include <lunaix/mm/valloc.h>
6 #include <lunaix/spike.h>
7 #include <lunaix/bcache.h>
8 #include <lunaix/syslog.h>
12 #define pcache_obj(bcache) container_of(bcache, struct pcache, cache)
14 void pcache_release_page(struct pcache* pcache, struct pcache_pg* page);
15 void pcache_set_dirty(struct pcache* pcache, struct pcache_pg* pg);
17 static bcache_zone_t pagecached_zone = NULL;
20 __pcache_sync(struct bcache* bc, unsigned long tag, void* data)
24 cache = pcache_obj(bc);
25 pcache_commit(cache->master, (struct pcache_pg*)data);
29 __pcache_try_release(struct bcache* bc, void* data)
31 struct pcache_pg* page;
33 page = (struct pcache_pg*)data;
34 pcache_release_page(pcache_obj(bc), page);
37 static struct bcache_ops cache_ops = {
38 .release_on_evict = __pcache_try_release,
39 .sync_cached = __pcache_sync
47 struct leaflet* leaflet = alloc_leaflet(0);
53 if (!(va = (ptr_t)vmap(leaflet, KERNEL_DATA))) {
54 leaflet_return(leaflet);
62 pcache_free_page(void* va)
64 pte_t* ptep = mkptep_va(VMS_SELF, (ptr_t)va);
65 pte_t pte = pte_at(ptep);
66 leaflet_return(pte_leaflet(pte));
70 pcache_init(struct pcache* pcache)
72 if (unlikely(!pagecached_zone)) {
73 pagecached_zone = bcache_create_zone("pcache");
76 llist_init_head(&pcache->dirty);
78 bcache_init_zone(&pcache->cache, pagecached_zone, 4, -1,
79 sizeof(struct pcache_pg), &cache_ops);
83 pcache_release_page(struct pcache* pcache, struct pcache_pg* page)
85 pcache_free_page(page->data);
93 pcache_new_page(struct pcache* pcache)
95 struct pcache_pg* ppg;
98 data_page = pcache_alloc_page();
103 ppg = vzalloc(sizeof(struct pcache_pg));
104 ppg->data = data_page;
110 pcache_set_dirty(struct pcache* pcache, struct pcache_pg* pg)
118 llist_append(&pcache->dirty, &pg->dirty_list);
122 __getpage_and_lock(struct pcache* pcache, unsigned int tag,
123 struct pcache_pg** page)
126 struct pcache_pg* pg;
128 if (bcache_tryget(&pcache->cache, tag, &cobj))
130 *page = (struct pcache_pg*)bcached_data(cobj);
134 pg = pcache_new_page(pcache);
145 __fill_page(struct v_inode* inode, struct pcache_pg* pg, unsigned int index)
147 return inode->default_fops->read_page(inode, pg->data, page_addr(index));
151 pcache_write(struct v_inode* inode, void* data, u32_t len, u32_t fpos)
154 unsigned int tag, off, wr_cnt;
155 unsigned int end = fpos + len;
156 struct pcache* pcache;
157 struct pcache_pg* pg;
160 pcache = inode->pg_cache;
162 while (fpos < end && errno >= 0) {
164 off = va_offset(fpos);
165 wr_cnt = MIN(end - fpos, PAGE_SIZE - off);
167 obj = __getpage_and_lock(pcache, tag, &pg);
170 errno = inode->default_fops->write(inode, data, fpos, wr_cnt);
174 // new page and unaligned write, then prepare for partial override
175 if (!obj && wr_cnt != PAGE_SIZE) {
176 errno = __fill_page(inode, pg, tag);
182 memcpy(offset(pg->data, off), data, wr_cnt);
183 pcache_set_dirty(pcache, pg);
188 bcache_put(&pcache->cache, tag, pg);
192 data = offset(data, wr_cnt);
196 return errno < 0 ? errno : (int)(len - (end - fpos));
200 pcache_read(struct v_inode* inode, void* data, u32_t len, u32_t fpos)
203 unsigned int tag, off, rd_cnt;
204 unsigned int end = fpos + len, size = 0;
205 struct pcache* pcache;
206 struct pcache_pg* pg;
209 pcache = inode->pg_cache;
211 while (fpos < page_upaligned(end)) {
213 off = va_offset(fpos);
215 obj = __getpage_and_lock(pcache, tag, &pg);
218 errno = __fill_page(inode, pg, tag);
223 end -= (PAGE_SIZE - errno);
226 rd_cnt = MIN(end - fpos, PAGE_SIZE - off);
227 memcpy(data, pg->data + off, rd_cnt);
232 bcache_put(&pcache->cache, tag, pg);
237 fpos = page_aligned(fpos + PAGE_SIZE);
240 return errno < 0 ? errno : (int)size;
244 pcache_release(struct pcache* pcache)
246 bcache_free(&pcache->cache);
250 pcache_commit(struct v_inode* inode, struct pcache_pg* page)
257 unsigned int fpos = page_addr(page->index);
259 errno = inode->default_fops->write_page(inode, page->data, fpos);
262 llist_delete(&page->dirty_list);
263 inode->pg_cache->n_dirty--;
270 pcache_commit_all(struct v_inode* inode)
272 struct pcache* cache = inode->pg_cache;
277 struct pcache_pg *pos, *n;
278 llist_for_each(pos, n, &cache->dirty, dirty_list)
280 pcache_commit(inode, pos);