9ns: kfs: Give the CPIO blob to the base arena
[akaros.git] / kern / drivers / dev / kfs.c
1 /* Copyright (c) 2018 Google Inc
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * See LICENSE for details.
4  *
5  * #kfs, in-memory ram filesystem, pulling from the kernel's embedded CPIO
6  */
7
8 #include <ns.h>
9 #include <kmalloc.h>
10 #include <string.h>
11 #include <stdio.h>
12 #include <assert.h>
13 #include <error.h>
14 #include <tree_file.h>
15 #include <pmap.h>
16 #include <cpio.h>
17
18 struct dev kfs_devtab;
19
20 struct kfs {
21         struct tree_filesystem          tfs;
22         atomic_t                                        qid;
23 } kfs;
24
25 static uint64_t kfs_get_qid_path(void)
26 {
27         return atomic_fetch_and_add(&kfs.qid, 1);
28 }
29
30 static char *devname(void)
31 {
32         return kfs_devtab.name;
33 }
34
35 static void kfs_tf_free(struct tree_file *tf)
36 {
37         /* We have nothing special hanging off the TF */
38 }
39
40 static void kfs_tf_unlink(struct tree_file *parent, struct tree_file *child)
41 {
42         /* This is the "+1 for existing" ref. */
43         tf_kref_put(child);
44 }
45
46 static void __kfs_tf_init(struct tree_file *tf, int dir_type, int dir_dev,
47                           struct username *user, int perm)
48 {
49         struct dir *dir = &tf->file.dir;
50
51         fs_file_init_dir(&tf->file, dir_type, dir_dev, user, perm);
52         dir->qid.path = kfs_get_qid_path();
53         dir->qid.vers = 0;
54         /* This is the "+1 for existing" ref.  There is no backing store for the FS,
55          * such as a disk or 9p, so we can't get rid of a file until it is unlinked
56          * and decreffed.  Note that KFS doesn't use pruners or anything else. */
57         __kref_get(&tf->kref, 1);
58 }
59
60 /* Note: If your TFS doesn't support symlinks, you need to error out */
61 static void kfs_tf_create(struct tree_file *parent, struct tree_file *child,
62                           int perm)
63 {
64         __kfs_tf_init(child, parent->file.dir.type, parent->file.dir.dev, &eve,
65                       perm);
66 }
67
68 static void kfs_tf_rename(struct tree_file *tf, struct tree_file *old_parent,
69                           struct tree_file *new_parent, const char *name,
70                           int flags)
71 {
72         /* We don't have a backend, so we don't need to do anything additional for
73          * rename. */
74 }
75
76 static bool kfs_tf_has_children(struct tree_file *parent)
77 {
78         /* The tree_file parent list is complete and not merely a cache for a real
79          * backend. */
80         return !list_empty(&parent->children);
81 }
82
83 struct tree_file_ops kfs_tf_ops = {
84         .free = kfs_tf_free,
85         .unlink = kfs_tf_unlink,
86         .lookup = NULL,
87         .create = kfs_tf_create,
88         .rename = kfs_tf_rename,
89         .has_children = kfs_tf_has_children,
90 };
91
92 /* Fills page with its contents from its backing store file.  For KFS, that
93  * means we're creating or extending a file, and the contents are 0.  Note the
94  * page/offset might be beyond the current file length, based on the current
95  * pagemap code. */
96 static int kfs_pm_readpage(struct page_map *pm, struct page *pg)
97 {
98         memset(page2kva(pg), 0, PGSIZE);
99         atomic_or(&pg->pg_flags, PG_UPTODATE);
100         /* Pretend that we blocked while filing this page.  This catches a lot of
101          * bugs.  It does slightly slow down the kernel, but it's only when filling
102          * the page cache, and considering we are using a RAMFS, you shouldn't
103          * measure things that actually rely on KFS's performance. */
104         kthread_usleep(1);
105         return 0;
106 }
107
108 /* Meant to take the page from PM and flush to backing store.  There is no
109  * backing store. */
110 static int kfs_pm_writepage(struct page_map *pm, struct page *pg)
111 {
112         return 0;
113 }
114
115 static void kfs_fs_punch_hole(struct fs_file *f, off64_t begin, off64_t end)
116 {
117 }
118
119 static bool kfs_fs_can_grow_to(struct fs_file *f, size_t len)
120 {
121         /* TODO: implement some sort of memory limit */
122         return true;
123 }
124
125 struct fs_file_ops kfs_fs_ops = {
126         .readpage = kfs_pm_readpage,
127         .writepage = kfs_pm_writepage,
128         .punch_hole = kfs_fs_punch_hole,
129         .can_grow_to = kfs_fs_can_grow_to,
130 };
131
132 /* Consumes root's chan, even on error. */
133 static struct chan *__add_kfs_dir(struct chan *root, char *path,
134                                   struct cpio_bin_hdr *c_bhdr)
135 {
136         ERRSTACK(1);
137         struct chan *c;
138
139         if (waserror()) {
140                 warn("failed to add %s", path);
141                 cclose(root);
142                 poperror();
143                 return NULL;
144         }
145         c = namec_from(root, path, Acreate, O_EXCL, DMDIR | c_bhdr->c_mode, NULL);
146         poperror();
147         return c;
148 }
149
150 static struct chan *__add_kfs_symlink(struct chan *root, char *path,
151                                       struct cpio_bin_hdr *c_bhdr)
152 {
153         ERRSTACK(1);
154         struct chan *c;
155         char target[c_bhdr->c_filesize + 1];
156
157         if (waserror()) {
158                 warn("failed to add %s", path);
159                 cclose(root);
160                 poperror();
161                 return NULL;
162         }
163         strncpy(target, c_bhdr->c_filestart, c_bhdr->c_filesize);
164         target[c_bhdr->c_filesize] = 0;
165         c = namec_from(root, path, Acreate, O_EXCL,
166                        DMSYMLINK | S_IRWXU | S_IRWXG | S_IRWXO, target);
167         poperror();
168         return c;
169 }
170
171 static struct chan *__add_kfs_file(struct chan *root, char *path,
172                                    struct cpio_bin_hdr *c_bhdr)
173 {
174         ERRSTACK(1);
175         struct chan *c;
176         off64_t offset = 0;
177         size_t ret, amt = c_bhdr->c_filesize;
178         void *buf = c_bhdr->c_filestart;
179
180         if (waserror()) {
181                 warn("failed to add %s", path);
182                 cclose(root);
183                 poperror();
184                 return NULL;
185         }
186         c = namec_from(root, path, Acreate, O_EXCL | O_RDWR, c_bhdr->c_mode, NULL);
187         poperror();
188         if (waserror()) {
189                 warn("failed to modify %s", path);
190                 cclose(c);
191                 poperror();
192                 return NULL;
193         }
194         while (amt) {
195                 ret = devtab[c->type].write(c, buf + offset, amt, offset);
196                 amt -= ret;
197                 offset += ret;
198         }
199         poperror();
200         return c;
201 }
202
203 static int add_kfs_entry(struct cpio_bin_hdr *c_bhdr, void *cb_arg)
204 {
205         struct tree_file *root = cb_arg;
206         char *path = c_bhdr->c_filename;
207         struct chan *c;
208         struct tree_file *tf;
209         struct timespec ts;
210
211         /* Root of the FS, already part of KFS */
212         if (!strcmp(path, "."))
213                 return 0;
214         c = tree_file_alloc_chan(root, &kfs_devtab, "#kfs");
215         switch (c_bhdr->c_mode & CPIO_FILE_MASK) {
216         case (CPIO_DIRECTORY):
217                 c = __add_kfs_dir(c, path, c_bhdr);
218                 break;
219         case (CPIO_SYMLINK):
220                 c = __add_kfs_symlink(c, path, c_bhdr);
221                 break;
222         case (CPIO_REG_FILE):
223                 c = __add_kfs_file(c, path, c_bhdr);
224                 break;
225         default:
226                 cclose(c);
227                 printk("Unknown file type %d in the CPIO!",
228                        c_bhdr->c_mode & CPIO_FILE_MASK);
229                 return -1;
230         }
231         if (!c)
232                 return -1;
233         tf = chan_to_tree_file(c);
234         ts.tv_sec = c_bhdr->c_mtime;
235         ts.tv_nsec = 0;
236         /* Lockless */
237         __set_acmtime_to(&tf->file, FSF_ATIME | FSF_BTIME | FSF_CTIME | FSF_MTIME,
238                          &ts);
239         /* TODO: consider UID/GID.  Right now, everything is owned by eve. */
240         cclose(c);
241         return 0;
242 }
243
244 struct cpio_info {
245         void *base;
246         size_t sz;
247 };
248
249 static void kfs_get_cpio_info(struct cpio_info *ci)
250 {
251         extern uint8_t _binary_obj_kern_initramfs_cpio_size[];
252         extern uint8_t _binary_obj_kern_initramfs_cpio_start[];
253
254         ci->base = (void*)_binary_obj_kern_initramfs_cpio_start;
255         ci->sz = (size_t)_binary_obj_kern_initramfs_cpio_size;
256 }
257
258 static void kfs_extract_cpio(struct cpio_info *ci)
259 {
260         parse_cpio_entries(ci->base, ci->sz, add_kfs_entry, kfs.tfs.root);
261 }
262
263 static void kfs_free_cpio(struct cpio_info *ci)
264 {
265         void *base = ci->base;
266         size_t sz = ci->sz;
267
268         /* The base arena requires page aligned, page sized segments. */
269         sz -= ROUNDUP(base, PGSIZE) - base;
270         sz = ROUNDDOWN(sz, PGSIZE);
271         base = ROUNDUP(base, PGSIZE);
272         /* Careful - the CPIO is part of the kernel blob and a code address. */
273         base = KBASEADDR(base);
274         printk("Freeing %d MB of CPIO RAM\n", sz >> 20);
275         arena_add(base_arena, base, sz, MEM_WAIT);
276 }
277
278 static void kfs_init(void)
279 {
280         struct tree_filesystem *tfs = &kfs.tfs;
281         struct cpio_info ci[1];
282
283         /* This gives us one ref on tfs->root. */
284         tfs_init(tfs);
285         tfs->tf_ops = kfs_tf_ops;
286         tfs->fs_ops = kfs_fs_ops;
287         /* Note this gives us the "+1 for existing" ref on tfs->root. */
288         __kfs_tf_init(tfs->root, &kfs_devtab - devtab, 0, &eve, DMDIR | 0777);
289         /* Other devices might want to create things like kthreads that run the LRU
290          * pruner or PM sweeper. */
291         kfs_get_cpio_info(ci);
292         kfs_extract_cpio(ci);
293         kfs_free_cpio(ci);
294         /* This has another kref.  Note that each attach gets a ref and each new
295          * process gets a ref. */
296         kern_slash = tree_file_alloc_chan(kfs.tfs.root, &kfs_devtab, "/");
297 }
298
299 static struct chan *kfs_attach(char *spec)
300 {
301         /* The root TF has a new kref for the attach chan */
302         return tree_file_alloc_chan(kfs.tfs.root, &kfs_devtab, "#kfs");
303 }
304
305 struct dev kfs_devtab __devtab = {
306         .name = "kfs",
307         .reset = devreset,
308         .init = kfs_init,
309         .shutdown = devshutdown,
310         .attach = kfs_attach,
311         .walk = tree_chan_walk,
312         .stat = tree_chan_stat,
313         .open = tree_chan_open,
314         .create = tree_chan_create,
315         .close = tree_chan_close,
316         .read = tree_chan_read,
317         .bread = devbread,
318         .write = tree_chan_write,
319         .bwrite = devbwrite,
320         .remove = tree_chan_remove,
321         .rename = tree_chan_rename,
322         .wstat = tree_chan_wstat,
323         .power = devpower,
324         .chaninfo = devchaninfo,
325         .mmap = tree_chan_mmap,
326 };