Use readstr() for #device text buffers
[akaros.git] / kern / drivers / dev / tmpfs.c
1 /* Copyright (c) 2018 Google Inc
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * See LICENSE for details.
4  *
5  * #tmpfs, in-memory ram filesystem instance.
6  *
7  * Compared to #kfs, there can be many #tmpfs instances, all of which are
8  * independent and clean themeselves up when they are detached and the last chan
9  * is closed.  A few notes:
10  * - The FS root ("/") can't be removed.  When you unmount, it gets
11  *   closed/decreffed.
12  * - The tmpfs will exist for the life of any open chans, including the mount.
13  *   You can e.g. cd into a subdir, unmount /, and the tmpfs will stay alive til
14  *   you leave the directory (basically close the chan).  You can do the same
15  *   thing with open_at().
16  * - The root tree_file will not be deleted so long as you have an open chan.
17  *   Any open chan on a subdir/subfile will hold refs on the root.  The mount
18  *   point will also hold those refs.  We also hold an additional +1 on the root
19  *   TF, which we drop once we have no users and we've purged the tree. */
20
21 #include <ns.h>
22 #include <kmalloc.h>
23 #include <string.h>
24 #include <stdio.h>
25 #include <assert.h>
26 #include <error.h>
27 #include <tree_file.h>
28 #include <pmap.h>
29 #include <cpio.h>
30
31 struct dev tmpfs_devtab;
32
33 struct tmpfs {
34         struct tree_filesystem          tfs;
35         atomic_t                                        qid;
36         struct kref                                     users;
37 };
38
39 static uint64_t tmpfs_get_qid_path(struct tmpfs *tmpfs)
40 {
41         return atomic_fetch_and_add(&tmpfs->qid, 1);
42 }
43
44 static char *devname(void)
45 {
46         return tmpfs_devtab.name;
47 }
48
49 static void tmpfs_tf_free(struct tree_file *tf)
50 {
51         /* We have nothing special hanging off the TF */
52 }
53
54 static void tmpfs_tf_unlink(struct tree_file *parent, struct tree_file *child)
55 {
56         /* This is the "+1 for existing" ref. */
57         tf_kref_put(child);
58 }
59
60 static void __tmpfs_tf_init(struct tree_file *tf, int dir_type, int dir_dev,
61                             struct username *user, int perm)
62 {
63         struct dir *dir = &tf->file.dir;
64
65         fs_file_init_dir(&tf->file, dir_type, dir_dev, user, perm);
66         dir->qid.path = tmpfs_get_qid_path((struct tmpfs*)tf->tfs);
67         dir->qid.vers = 0;
68         /* This is the "+1 for existing" ref.  There is no backing store for the FS,
69          * such as a disk or 9p, so we can't get rid of a file until it is unlinked
70          * and decreffed.  Note that KFS doesn't use pruners or anything else. */
71         __kref_get(&tf->kref, 1);
72 }
73
74 /* Note: If your TFS doesn't support symlinks, you need to error out */
75 static void tmpfs_tf_create(struct tree_file *parent, struct tree_file *child,
76                             int perm)
77 {
78         __tmpfs_tf_init(child, parent->file.dir.type, parent->file.dir.dev, &eve,
79                         perm);
80 }
81
82 static void tmpfs_tf_rename(struct tree_file *tf, struct tree_file *old_parent,
83                             struct tree_file *new_parent, const char *name,
84                             int flags)
85 {
86         /* We don't have a backend, so we don't need to do anything additional for
87          * rename. */
88 }
89
90 static bool tmpfs_tf_has_children(struct tree_file *parent)
91 {
92         /* The tree_file parent list is complete and not merely a cache for a real
93          * backend. */
94         return !list_empty(&parent->children);
95 }
96
97 struct tree_file_ops tmpfs_tf_ops = {
98         .free = tmpfs_tf_free,
99         .unlink = tmpfs_tf_unlink,
100         .lookup = NULL,
101         .create = tmpfs_tf_create,
102         .rename = tmpfs_tf_rename,
103         .has_children = tmpfs_tf_has_children,
104 };
105
106 /* Fills page with its contents from its backing store file.  For KFS, that
107  * means we're creating or extending a file, and the contents are 0.  Note the
108  * page/offset might be beyond the current file length, based on the current
109  * pagemap code. */
110 static int tmpfs_pm_readpage(struct page_map *pm, struct page *pg)
111 {
112         memset(page2kva(pg), 0, PGSIZE);
113         atomic_or(&pg->pg_flags, PG_UPTODATE);
114         /* Pretend that we blocked while filing this page.  This catches a lot of
115          * bugs.  It does slightly slow down the kernel, but it's only when filling
116          * the page cache, and considering we are using a RAMFS, you shouldn't
117          * measure things that actually rely on KFS's performance. */
118         kthread_usleep(1);
119         return 0;
120 }
121
122 /* Meant to take the page from PM and flush to backing store.  There is no
123  * backing store. */
124 static int tmpfs_pm_writepage(struct page_map *pm, struct page *pg)
125 {
126         return 0;
127 }
128
129 static void tmpfs_fs_punch_hole(struct fs_file *f, off64_t begin, off64_t end)
130 {
131 }
132
133 static bool tmpfs_fs_can_grow_to(struct fs_file *f, size_t len)
134 {
135         /* TODO: implement some sort of memory limit */
136         return true;
137 }
138
139 struct fs_file_ops tmpfs_fs_ops = {
140         .readpage = tmpfs_pm_readpage,
141         .writepage = tmpfs_pm_writepage,
142         .punch_hole = tmpfs_fs_punch_hole,
143         .can_grow_to = tmpfs_fs_can_grow_to,
144 };
145
146 static void purge_cb(struct tree_file *tf)
147 {
148         /* this is the +1 for existing */
149         tf_kref_put(tf);
150 }
151
152 static void tmpfs_release(struct kref *kref)
153 {
154         struct tmpfs *tmpfs = container_of(kref, struct tmpfs, users);
155
156         tfs_frontend_purge(&tmpfs->tfs, purge_cb);
157         /* this is the ref from attach */
158         assert(kref_refcnt(&tmpfs->tfs.root->kref) == 1);
159         tf_kref_put(tmpfs->tfs.root);
160         /* ensures __tf_free() happens before tfs_destroy */
161         rcu_barrier();
162         tfs_destroy(&tmpfs->tfs);
163         kfree(tmpfs);
164 }
165
166 static struct tmpfs *chan_to_tmpfs(struct chan *c)
167 {
168         struct tree_file *tf = chan_to_tree_file(c);
169
170         return (struct tmpfs*)(tf->tfs);
171 }
172
173 static void incref_tmpfs_chan(struct chan *c)
174 {
175         kref_get(&chan_to_tmpfs(c)->users, 1);
176 }
177
178 static void decref_tmpfs_chan(struct chan *c)
179 {
180         kref_put(&chan_to_tmpfs(c)->users);
181 }
182
183 static struct chan *tmpfs_attach(char *spec)
184 {
185         struct tree_filesystem *tfs = kzmalloc(sizeof(struct tmpfs), MEM_WAIT);
186         struct tmpfs *tmpfs = (struct tmpfs*)tfs;
187
188         /* All distinct chans get a ref on the filesystem, so that we can destroy it
189          * when the last user disconnects/closes. */
190         kref_init(&tmpfs->users, tmpfs_release, 1);
191
192         /* This gives us one ref on root, dropped during tmpfs_release(). */
193         tfs_init(tfs);
194         tfs->tf_ops = tmpfs_tf_ops;
195         tfs->fs_ops = tmpfs_fs_ops;
196
197         /* This gives us an extra refcnt on tfs->root.  This is "+1 for existing."
198          * It is decreffed during the purge CB. */
199         __tmpfs_tf_init(tfs->root, &tmpfs_devtab - devtab, 0, &eve, DMDIR | 0777);
200         /* This also increfs, copying tfs->root's ref for the chan it returns. */
201         return tree_file_alloc_chan(tfs->root, &tmpfs_devtab, "#tmpfs");
202 }
203
204 static struct walkqid *tmpfs_walk(struct chan *c, struct chan *nc, char **name,
205                                   unsigned int nname)
206 {
207         struct walkqid *wq = tree_chan_walk(c, nc, name, nname);
208
209         if (wq && wq->clone && (wq->clone != c))
210                 incref_tmpfs_chan(wq->clone);
211         return wq;
212 }
213
214 static void tmpfs_close(struct chan *c)
215 {
216         tree_chan_close(c);
217         decref_tmpfs_chan(c);
218 }
219
220 static void tmpfs_remove(struct chan *c)
221 {
222         ERRSTACK(1);
223         struct tmpfs *tmpfs = chan_to_tmpfs(c);
224
225         /* This is a bit of a pain - when remove fails, we won't get a chance to
226          * close the chan.  See notes in tree_chan_remove() and sysremove(). */
227         if (waserror()) {
228                 kref_put(&tmpfs->users);
229                 nexterror();
230         }
231         tree_chan_remove(c);
232         kref_put(&tmpfs->users);
233         poperror();
234 }
235
236 void tmpfs_rename(struct chan *c, struct chan *new_p_c, const char *name,
237                   int flags)
238 {
239         struct tmpfs *tmpfs_old = chan_to_tmpfs(c);
240         struct tmpfs *tmpfs_new = chan_to_tmpfs(new_p_c);
241
242         /* namec checked that both were from the same device.  It is our
243          * responsibility to make sure they are the same version. */
244         if (tmpfs_old != tmpfs_new)
245                 error(EXDEV, "can't rename across tmpfs instances");
246         tree_chan_rename(c, new_p_c, name, flags);
247 }
248
249 static unsigned long tmpfs_chan_ctl(struct chan *c, int op, unsigned long a1,
250                                     unsigned long a2, unsigned long a3,
251                                     unsigned long a4)
252 {
253         switch (op) {
254         case CCTL_SYNC:
255                 return 0;
256         default:
257                 return tree_chan_ctl(c, op, a1, a2, a3, a4);
258         }
259 }
260
261 struct dev tmpfs_devtab __devtab = {
262         .name = "tmpfs",
263         .reset = devreset,
264         .init = devinit,
265         .shutdown = devshutdown,
266         .attach = tmpfs_attach,
267         .walk = tmpfs_walk,
268         .stat = tree_chan_stat,
269         .open = tree_chan_open,
270         .create = tree_chan_create,
271         .close = tmpfs_close,
272         .read = tree_chan_read,
273         .bread = devbread,
274         .write = tree_chan_write,
275         .bwrite = devbwrite,
276         .remove = tmpfs_remove,
277         .rename = tmpfs_rename,
278         .wstat = tree_chan_wstat,
279         .power = devpower,
280         .chaninfo = devchaninfo,
281         .mmap = tree_chan_mmap,
282         .chan_ctl = tmpfs_chan_ctl,
283 };