Merge branch 'vfs-dev'
[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/pmm.h>
6 #include <lunaix/mm/valloc.h>
7 #include <lunaix/mm/vmm.h>
8 #include <lunaix/spike.h>
9
10 #define PCACHE_DIRTY 0x1
11
12 static struct lru_zone* pcache_zone;
13
14 void
15 pcache_init(struct pcache* pcache)
16 {
17     btrie_init(&pcache->tree, PG_SIZE_BITS);
18     llist_init_head(&pcache->dirty);
19     llist_init_head(&pcache->pages);
20     pcache_zone = lru_new_zone();
21 }
22
23 void
24 pcache_release_page(struct pcache* pcache, struct pcache_pg* page)
25 {
26     vfree(page->pg);
27
28     llist_delete(&page->pg_list);
29
30     vfree(page);
31
32     pcache->n_pages--;
33 }
34
35 void
36 pcache_evict(struct pcache* pcache)
37 {
38     struct pcache_pg* page =
39       container_of(lru_evict_one(pcache_zone), struct pcache_pg, lru);
40
41     if (!page)
42         return;
43
44     pcache_invalidate(pcache, page);
45 }
46
47 struct pcache_pg*
48 pcache_new_page(struct pcache* pcache, uint32_t index)
49 {
50     struct pcache_pg* ppg = vzalloc(sizeof(struct pcache_pg));
51     void* pg = valloc(PG_SIZE);
52
53     if (!ppg || !pg) {
54         pcache_evict(pcache);
55         if (!ppg && !(ppg = vzalloc(sizeof(struct pcache_pg)))) {
56             return NULL;
57         }
58
59         if (!pg && !(pg = valloc(PG_SIZE))) {
60             return NULL;
61         }
62     }
63
64     ppg->pg = pg;
65
66     llist_append(&pcache->pages, &ppg->pg_list);
67     btrie_set(&pcache->tree, index, ppg);
68
69     return ppg;
70 }
71
72 void
73 pcache_set_dirty(struct pcache* pcache, struct pcache_pg* pg)
74 {
75     if (!(pg->flags & PCACHE_DIRTY)) {
76         pg->flags |= PCACHE_DIRTY;
77         pcache->n_dirty++;
78         llist_append(&pcache->dirty, &pg->dirty_list);
79     }
80 }
81
82 struct pcache_pg*
83 pcache_get_page(struct pcache* pcache,
84                 uint32_t index,
85                 uint32_t* offset,
86                 struct pcache_pg** page)
87 {
88     struct pcache_pg* pg = btrie_get(&pcache->tree, index);
89     int is_new = 0;
90     *offset = index & ((1 << pcache->tree.truncated) - 1);
91     if (!pg && (pg = pcache_new_page(pcache, index))) {
92         pg->fpos = index - *offset;
93         pcache->n_pages++;
94         is_new = 1;
95     }
96     if (pg)
97         lru_use_one(pcache_zone, &pg->lru);
98     *page = pg;
99     return is_new;
100 }
101
102 int
103 pcache_write(struct v_inode* inode, void* data, uint32_t len, uint32_t fpos)
104 {
105     uint32_t pg_off, buf_off = 0;
106     struct pcache* pcache = inode->pg_cache;
107     struct pcache_pg* pg;
108
109     while (buf_off < len) {
110         pcache_get_page(pcache, fpos, &pg_off, &pg);
111         if (!pg) {
112             return ENOMEM;
113         }
114
115         uint32_t wr_bytes = MIN(PG_SIZE - pg_off, len - buf_off);
116         memcpy(pg->pg + pg_off, (data + buf_off), wr_bytes);
117
118         pcache_set_dirty(pcache, pg);
119
120         buf_off += wr_bytes;
121         fpos += wr_bytes;
122     }
123
124     return buf_off;
125 }
126
127 int
128 pcache_read(struct v_inode* inode, void* data, uint32_t len, uint32_t fpos)
129 {
130     uint32_t pg_off, buf_off = 0, new_pg = 0;
131     int errno = 0;
132     struct pcache* pcache = inode->pg_cache;
133     struct pcache_pg* pg;
134
135     while (buf_off < len) {
136         if (pcache_get_page(pcache, fpos, &pg_off, &pg)) {
137
138             if (!pg) {
139                 return ENOMEM;
140             }
141
142             // Filling up the page
143             errno = inode->default_fops.read(inode, pg->pg, PG_SIZE, pg->fpos);
144             if (errno >= 0 && errno < PG_SIZE) {
145                 // EOF
146                 len = buf_off + errno;
147             } else if (errno < 0) {
148                 break;
149             }
150         }
151         uint32_t rd_bytes = MIN(PG_SIZE - pg_off, len - buf_off);
152         memcpy((data + buf_off), pg->pg + pg_off, rd_bytes);
153
154         buf_off += rd_bytes;
155         fpos += rd_bytes;
156     }
157
158     return errno < 0 ? errno : buf_off;
159 }
160
161 void
162 pcache_release(struct pcache* pcache)
163 {
164     struct pcache_pg *pos, *n;
165     llist_for_each(pos, n, &pcache->pages, pg_list)
166     {
167         lru_remove(&pos->lru);
168         vfree(pos);
169     }
170
171     btrie_release(&pcache->tree);
172 }
173
174 int
175 pcache_commit(struct v_inode* inode, struct pcache_pg* page)
176 {
177     if (!(page->flags & PCACHE_DIRTY)) {
178         return;
179     }
180
181     int errno = inode->default_fops.write(inode, page->pg, PG_SIZE, page->fpos);
182
183     if (!errno) {
184         page->flags &= ~PCACHE_DIRTY;
185         llist_delete(&page->dirty_list);
186         inode->pg_cache->n_dirty--;
187     }
188
189     return errno;
190 }
191
192 void
193 pcache_commit_all(struct v_inode* inode)
194 {
195     struct pcache* cache = inode->pg_cache;
196     struct pcache_pg *pos, *n;
197
198     llist_for_each(pos, n, &cache->dirty, dirty_list)
199     {
200         pcache_commit(inode, pos);
201     }
202 }
203
204 void
205 pcache_invalidate(struct pcache* pcache, struct pcache_pg* page)
206 {
207     pcache_commit(pcache->master, page);
208     pcache_release_page(pcache, page);
209 }