rework parsing of interupt-map in interrupt node.
[lunaix-os.git] / lunaix-os / kernel / fs / pcache.c
1 #include <klibc/string.h>
2 #include <lunaix/ds/btrie.h>
3 #include <lunaix/fs.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>
9
10 LOG_MODULE("pcache")
11
12 #define pcache_obj(bcache) container_of(bcache, struct pcache, cache)
13
14 void pcache_release_page(struct pcache* pcache, struct pcache_pg* page);
15 void pcache_set_dirty(struct pcache* pcache, struct pcache_pg* pg);
16
17 static bcache_zone_t pagecached_zone = NULL;
18
19 static void
20 __pcache_sync(struct bcache* bc, unsigned long tag, void* data)
21 {
22     struct pcache* cache;
23
24     cache = pcache_obj(bc);
25     pcache_commit(cache->master, (struct pcache_pg*)data);
26 }
27
28 static void
29 __pcache_try_release(struct bcache* bc, void* data)
30 {
31     struct pcache_pg* page;
32     
33     page = (struct pcache_pg*)data;
34     pcache_release_page(pcache_obj(bc), page);
35 }
36
37 static struct bcache_ops cache_ops = {
38     .release_on_evict = __pcache_try_release,
39     .sync_cached = __pcache_sync
40 };
41
42 static void*
43 pcache_alloc_page()
44 {
45     int i = 0;
46     ptr_t va = 0;
47     struct leaflet* leaflet = alloc_leaflet(0);
48
49     if (!leaflet) {
50         return NULL;
51     }
52
53     if (!(va = (ptr_t)vmap(leaflet, KERNEL_DATA))) {
54         leaflet_return(leaflet);
55         return NULL;
56     }
57
58     return (void*)va;
59 }
60
61 static void
62 pcache_free_page(void* va)
63 {
64     pte_t* ptep = mkptep_va(VMS_SELF, (ptr_t)va);
65     pte_t pte = pte_at(ptep);
66     leaflet_return(pte_leaflet(pte));
67 }
68
69 void
70 pcache_init(struct pcache* pcache)
71 {
72     if (unlikely(!pagecached_zone)) {
73         pagecached_zone = bcache_create_zone("pcache");
74     }
75
76     llist_init_head(&pcache->dirty);
77
78     bcache_init_zone(&pcache->cache, pagecached_zone, 4, -1, 
79                      sizeof(struct pcache_pg), &cache_ops);
80 }
81
82 void
83 pcache_release_page(struct pcache* pcache, struct pcache_pg* page)
84 {
85     pcache_free_page(page->data);
86
87     vfree(page);
88
89     pcache->n_pages--;
90 }
91
92 struct pcache_pg*
93 pcache_new_page(struct pcache* pcache)
94 {
95     struct pcache_pg* ppg;
96     void* data_page;
97     
98     data_page = pcache_alloc_page();
99     if (!data_page) {
100         return NULL;
101     }
102
103     ppg = vzalloc(sizeof(struct pcache_pg));
104     ppg->data = data_page;
105
106     return ppg;
107 }
108
109 void
110 pcache_set_dirty(struct pcache* pcache, struct pcache_pg* pg)
111 {
112     if (pg->dirty) {
113         return;
114     }
115
116     pg->dirty = true;
117     pcache->n_dirty++;
118     llist_append(&pcache->dirty, &pg->dirty_list);
119 }
120
121 static bcobj_t
122 __getpage_and_lock(struct pcache* pcache, unsigned int tag, 
123                    struct pcache_pg** page)
124 {
125     bcobj_t cobj;
126     struct pcache_pg* pg;
127
128     if (bcache_tryget(&pcache->cache, tag, &cobj))
129     {
130         *page = (struct pcache_pg*)bcached_data(cobj);
131         return cobj;
132     }
133
134     pg = pcache_new_page(pcache);
135     if (pg) {
136         pg->index = tag;
137     }
138
139     *page = pg;
140
141     return NULL;
142 }
143
144 static inline int
145 __fill_page(struct v_inode* inode, struct pcache_pg* pg, unsigned int index)
146 {
147     return inode->default_fops->read_page(inode, pg->data, page_addr(index));
148 }
149
150 int
151 pcache_write(struct v_inode* inode, void* data, u32_t len, u32_t fpos)
152 {
153     int errno = 0;
154     unsigned int tag, off, wr_cnt;
155     unsigned int end = fpos + len;
156     struct pcache* pcache;
157     struct pcache_pg* pg;
158     bcobj_t obj;
159
160     pcache = inode->pg_cache;
161     
162     while (fpos < end && errno >= 0) {
163         tag = pfn(fpos);
164         off = va_offset(fpos);
165         wr_cnt = MIN(end - fpos, PAGE_SIZE - off);
166
167         obj = __getpage_and_lock(pcache, tag, &pg);
168
169         if (!obj && !pg) {
170             errno = inode->default_fops->write(inode, data, fpos, wr_cnt);
171             goto cont;
172         }
173
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);
177             if (errno < 0) {
178                 return errno;
179             }
180         }
181         
182         memcpy(offset(pg->data, off), data, wr_cnt);
183         pcache_set_dirty(pcache, pg);
184
185         if (obj) {
186             bcache_return(obj);
187         } else {
188             bcache_put(&pcache->cache, tag, pg);
189         }
190
191 cont:
192         data  = offset(data, wr_cnt);
193         fpos += wr_cnt;
194     }
195
196     return errno < 0 ? errno : (int)(len - (end - fpos));
197 }
198
199 int
200 pcache_read(struct v_inode* inode, void* data, u32_t len, u32_t fpos)
201 {
202     int errno = 0;
203     unsigned int tag, off, rd_cnt;
204     unsigned int end = fpos + len, size = 0;
205     struct pcache* pcache;
206     struct pcache_pg* pg;
207     bcobj_t obj;
208
209     pcache = inode->pg_cache;
210
211     while (fpos < page_upaligned(end)) {
212         tag = pfn(fpos);
213         off = va_offset(fpos);
214
215         obj = __getpage_and_lock(pcache, tag, &pg);
216
217         if (!obj) {
218             errno = __fill_page(inode, pg, tag);
219             if (errno < 0) {
220                 return errno;
221             }
222
223             end -= (PAGE_SIZE - errno);
224         }
225
226         rd_cnt = MIN(end - fpos, PAGE_SIZE - off);
227         memcpy(data, pg->data + off, rd_cnt);
228
229         if (obj) {
230             bcache_return(obj);
231         } else {
232             bcache_put(&pcache->cache, tag, pg);
233         }
234
235         data += rd_cnt;
236         size += rd_cnt;
237         fpos = page_aligned(fpos + PAGE_SIZE);
238     }
239
240     return errno < 0 ? errno : (int)size;
241 }
242
243 void
244 pcache_release(struct pcache* pcache)
245 {
246     bcache_free(&pcache->cache);
247 }
248
249 int
250 pcache_commit(struct v_inode* inode, struct pcache_pg* page)
251 {
252     if (!page->dirty) {
253         return 0;
254     }
255
256     int errno;
257     unsigned int fpos = page_addr(page->index);
258     
259     errno = inode->default_fops->write_page(inode, page->data, fpos);
260     if (!errno) {
261         page->dirty = false;
262         llist_delete(&page->dirty_list);
263         inode->pg_cache->n_dirty--;
264     }
265
266     return errno;
267 }
268
269 void
270 pcache_commit_all(struct v_inode* inode)
271 {
272     struct pcache* cache = inode->pg_cache;
273     if (!cache) {
274         return;
275     }
276
277     struct pcache_pg *pos, *n;
278     llist_for_each(pos, n, &cache->dirty, dirty_list)
279     {
280         pcache_commit(inode, pos);
281     }
282 }