Page cache for memory mapped files
[akaros.git] / kern / src / vfs.c
1 /* Copyright (c) 2009, 2010 The Regents of the University of California
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * See LICENSE for details.
4  *
5  * Default implementations and global values for the VFS. */
6
7 #include <vfs.h> // keep this first
8 #include <sys/queue.h>
9 #include <assert.h>
10 #include <stdio.h>
11 #include <atomic.h>
12 #include <slab.h>
13 #include <kmalloc.h>
14 #include <kfs.h>
15
16 struct sb_tailq super_blocks = TAILQ_HEAD_INITIALIZER(super_blocks);
17 spinlock_t super_blocks_lock = SPINLOCK_INITIALIZER;
18 struct fs_type_tailq file_systems = TAILQ_HEAD_INITIALIZER(file_systems);
19 struct namespace default_ns;
20 // TODO: temp dcache, holds all dentries ever for now
21 struct dentry_slist dcache = SLIST_HEAD_INITIALIZER(dcache);
22 spinlock_t dcache_lock = SPINLOCK_INITIALIZER;
23
24 struct kmem_cache *dentry_kcache; // not to be confused with the dcache
25 struct kmem_cache *inode_kcache;
26 struct kmem_cache *file_kcache;
27
28 /* Mounts fs from dev_name at mnt_pt in namespace ns.  There could be no mnt_pt,
29  * such as with the root of (the default) namespace.  Not sure how it would work
30  * with multiple namespaces on the same FS yet.  Note if you mount the same FS
31  * multiple times, you only have one FS still (and one SB).  If we ever support
32  * that... */
33 struct vfsmount *mount_fs(struct fs_type *fs, char *dev_name,
34                           struct dentry *mnt_pt, int flags,
35                           struct namespace *ns)
36 {
37         struct super_block *sb;
38         struct vfsmount *vmnt = kmalloc(sizeof(struct vfsmount), 0);
39
40         /* Build the vfsmount, if there is no mnt_pt, mnt is the root vfsmount (for now).
41          * fields related to the actual FS, like the sb and the mnt_root are set in
42          * the fs-specific get_sb() call. */
43         if (!mnt_pt) {
44                 vmnt->mnt_parent = NULL;
45                 vmnt->mnt_mountpoint = NULL;
46         } else { /* common case, but won't be tested til we try to mount another FS */
47                 mnt_pt->d_mount_point = TRUE;
48                 mnt_pt->d_mounted_fs = vmnt;
49                 atomic_inc(&vmnt->mnt_refcnt); /* held by mnt_pt */
50                 vmnt->mnt_parent = mnt_pt->d_sb->s_mount;
51                 vmnt->mnt_mountpoint = mnt_pt;
52         }
53         TAILQ_INIT(&vmnt->mnt_child_mounts);
54         vmnt->mnt_flags = flags;
55         vmnt->mnt_devname = dev_name;
56         vmnt->mnt_namespace = ns;
57         atomic_inc(&ns->refcnt); /* held by vmnt */
58         atomic_set(&vmnt->mnt_refcnt, 1); /* for the ref in the NS tailq below */
59
60         /* Read in / create the SB */
61         sb = fs->get_sb(fs, flags, dev_name, vmnt);
62         if (!sb)
63                 panic("You're FS sucks");
64
65         /* TODO: consider moving this into get_sb or something, in case the SB
66          * already exists (mounting again) (if we support that) */
67         spin_lock(&super_blocks_lock);
68         TAILQ_INSERT_TAIL(&super_blocks, sb, s_list); /* storing a ref here... */
69         spin_unlock(&super_blocks_lock);
70
71         /* Update holding NS */
72         spin_lock(&ns->lock);
73         TAILQ_INSERT_TAIL(&ns->vfsmounts, vmnt, mnt_list);
74         spin_unlock(&ns->lock);
75         /* note to self: so, right after this point, the NS points to the root FS
76          * mount (we return the mnt, which gets assigned), the root mnt has a dentry
77          * for /, backed by an inode, with a SB prepped and in memory. */
78         return vmnt;
79 }
80
81 void vfs_init(void)
82 {
83         struct fs_type *fs;
84
85         dentry_kcache = kmem_cache_create("dentry", sizeof(struct dentry),
86                                           __alignof__(struct dentry), 0, 0, 0);
87         inode_kcache = kmem_cache_create("inode", sizeof(struct inode),
88                                          __alignof__(struct inode), 0, 0, 0);
89         file_kcache = kmem_cache_create("file", sizeof(struct file),
90                                         __alignof__(struct file), 0, 0, 0);
91
92         atomic_set(&default_ns.refcnt, 1); // default NS never dies, +1 to exist
93         spinlock_init(&default_ns.lock);
94         default_ns.root = NULL;
95         TAILQ_INIT(&default_ns.vfsmounts);
96
97         /* build list of all FS's in the system.  put yours here.  if this is ever
98          * done on the fly, we'll need to lock. */
99         TAILQ_INSERT_TAIL(&file_systems, &kfs_fs_type, list);
100         TAILQ_FOREACH(fs, &file_systems, list)
101                 printk("Supports the %s Filesystem\n", fs->name);
102
103         /* mounting KFS at the root (/), pending root= parameters */
104         // TODO: linux creates a temp root_fs, then mounts the real root onto that
105         default_ns.root = mount_fs(&kfs_fs_type, "RAM", NULL, 0, &default_ns);
106
107         printk("vfs_init() completed\n");
108         /*
109         put structs and friends in struct proc, and init in proc init
110         */
111         // LOOKUP: follow_mount, follow_link, etc
112         // pains in the ass for having .. or . in the middle of the path
113 }
114
115 /* Builds / populates the qstr of a dentry based on its d_iname.  If there is an
116  * l_name, (long), it will use that instead of the inline name.  This will
117  * probably change a bit. */
118 void qstr_builder(struct dentry *dentry, char *l_name)
119 {
120         dentry->d_name.name = l_name ? l_name : dentry->d_iname;
121         // TODO: pending what we actually do in d_hash
122         //dentry->d_name.hash = dentry->d_op->d_hash(dentry, &dentry->d_name); 
123         dentry->d_name.hash = 0xcafebabe;
124         dentry->d_name.len = strnlen(dentry->d_name.name, MAX_FILENAME_SZ);
125 }
126
127 /* Helper to alloc and initialize a generic superblock.  This handles all the
128  * VFS related things, like lists.  Each FS will need to handle its own things
129  * in it's *_get_sb(), usually involving reading off the disc. */
130 struct super_block *get_sb(void)
131 {
132         struct super_block *sb = kmalloc(sizeof(struct super_block), 0);
133         sb->s_dirty = FALSE;
134         spinlock_init(&sb->s_lock);
135         atomic_set(&sb->s_refcnt, 1); // for the ref passed out
136         TAILQ_INIT(&sb->s_inodes);
137         TAILQ_INIT(&sb->s_dirty_i);
138         TAILQ_INIT(&sb->s_io_wb);
139         SLIST_INIT(&sb->s_anon_d);
140         TAILQ_INIT(&sb->s_files);
141         sb->s_fs_info = 0; // can override somewhere else
142         return sb;
143 }
144
145 /* Final stages of initializing a super block, including creating and linking
146  * the root dentry, root inode, vmnt, and sb.  The d_op and root_ino are
147  * FS-specific, but otherwise it's FS-independent, tricky, and not worth having
148  * around multiple times.
149  *
150  * Not the world's best interface, so it's subject to change, esp since we're
151  * passing (now 3) FS-specific things. */
152 void init_sb(struct super_block *sb, struct vfsmount *vmnt,
153              struct dentry_operations *d_op, unsigned long root_ino,
154              void *d_fs_info)
155 {
156         /* Build and init the first dentry / inode.  The dentry ref is stored later
157          * by vfsmount's mnt_root.  The parent is dealt with later. */
158         struct dentry *d_root = get_dentry(sb, 0,  "/");        /* probably right */
159
160         /* a lot of here on down is normally done in lookup() */
161         d_root->d_op = d_op;
162         d_root->d_fs_info = d_fs_info;
163         struct inode *inode = sb->s_op->alloc_inode(sb);
164         if (!inode)
165                 panic("This FS sucks!");
166         d_root->d_inode = inode;
167         TAILQ_INSERT_TAIL(&inode->i_dentry, d_root, d_alias);
168         atomic_inc(&d_root->d_refcnt);                  /* held by the inode */
169         inode->i_ino = root_ino;
170         /* TODO: add the inode to the appropriate list (off i_list) */
171         /* TODO: do we need to read in the inode?  can we do this on demand? */
172         /* if this FS is already mounted, we'll need to do something different. */
173         sb->s_op->read_inode(inode);
174         /* Link the dentry and SB to the VFS mount */
175         vmnt->mnt_root = d_root;                                /* refcnt'd above */
176         vmnt->mnt_sb = sb;
177         /* If there is no mount point, there is no parent.  This is true only for
178          * the rootfs. */
179         if (vmnt->mnt_mountpoint) {
180                 d_root->d_parent = vmnt->mnt_mountpoint;        /* dentry of the root */
181                 atomic_inc(&vmnt->mnt_mountpoint->d_refcnt);/* held by d_root */
182         }
183         /* insert the dentry into the dentry cache.  when's the earliest we can?
184          * when's the earliest we should?  what about concurrent accesses to the
185          * same dentry?  should be locking the dentry... */
186         dcache_put(d_root); // TODO: should set a d_flag too
187 }
188
189 /* Helper to alloc and initialize a generic dentry.
190  *
191  * If the name is longer than the inline name, it will kmalloc a buffer, so
192  * don't worry about the storage for *name after calling this. */
193 struct dentry *get_dentry(struct super_block *sb, struct dentry *parent,
194                           char *name)
195 {
196         assert(name);
197         size_t name_len = strnlen(name, MAX_FILENAME_SZ);       /* not including \0! */
198         struct dentry *dentry = kmem_cache_alloc(dentry_kcache, 0);
199         char *l_name = 0;
200
201         //memset(dentry, 0, sizeof(struct dentry));
202         atomic_set(&dentry->d_refcnt, 1);       /* this ref is returned */
203         spinlock_init(&dentry->d_lock);
204         TAILQ_INIT(&dentry->d_subdirs);
205         dentry->d_time = 0;
206         dentry->d_sb = sb;                                      /* storing a ref here... */
207         dentry->d_mount_point = FALSE;
208         dentry->d_mounted_fs = 0;
209         dentry->d_parent = parent;
210         if (parent)                                                     /* no parent for rootfs mount */
211                 atomic_inc(&parent->d_refcnt);
212         dentry->d_flags = 0;                            /* related to its dcache state */
213         dentry->d_fs_info = 0;
214         SLIST_INIT(&dentry->d_bucket);
215         if (name_len < DNAME_INLINE_LEN) {
216                 strncpy(dentry->d_iname, name, name_len);
217                 dentry->d_iname[name_len] = '\0';
218                 qstr_builder(dentry, 0);
219         } else {
220                 l_name = kmalloc(name_len + 1, 0);
221                 assert(l_name);
222                 strncpy(l_name, name, name_len);
223                 l_name[name_len] = '\0';
224                 qstr_builder(dentry, l_name);
225         }
226         return dentry;
227 }
228
229 /* Adds a dentry to the dcache. */
230 void dcache_put(struct dentry *dentry)
231 {
232         // TODO: prob should do something with the dentry flags
233         spin_lock(&dcache_lock);
234         SLIST_INSERT_HEAD(&dcache, dentry, d_hash);
235         spin_unlock(&dcache_lock);
236 }
237
238 /* Looks up the index'th page in the page map, returning an incref'd reference,
239  * or 0 if it was not in the map. */
240 struct page *pm_find_page(struct page_map *pm, unsigned long index)
241 {
242         spin_lock(&pm->pm_tree_lock);
243         struct page *page = (struct page*)radix_lookup(&pm->pm_tree, index);
244         if (page)
245                 page_incref(page);
246         spin_unlock(&pm->pm_tree_lock);
247         return page;
248 }
249
250 /* Attempts to insert the page into the page_map, returns 0 for success, or an
251  * error code if there was one already (EEXIST) or we ran out of memory
252  * (ENOMEM).  On success, this will preemptively lock the page, and will also
253  * store a reference to the page in the pm. */
254 int pm_insert_page(struct page_map *pm, unsigned long index, struct page *page)
255 {
256         int error = 0;
257         spin_lock(&pm->pm_tree_lock);
258         error = radix_insert(&pm->pm_tree, index, page);
259         if (!error) {
260                 page_incref(page);
261                 page->pg_flags |= PG_LOCKED;
262                 page->pg_mapping = pm;
263                 page->pg_index = index;
264                 pm->pm_num_pages++;
265         }
266         spin_unlock(&pm->pm_tree_lock);
267         return error;
268 }
269
270 /* Removes the page, including its reference.  Not sure yet what interface we
271  * want to this (pm and index or page), and this has never been used.  There are
272  * also issues with when you want to call this, since a page in the cache may be
273  * mmap'd by someone else. */
274 int pm_remove_page(struct page_map *pm, struct page *page)
275 {
276         void *retval;
277         warn("pm_remove_page() hasn't been thought through or tested.");
278         spin_lock(&pm->pm_tree_lock);
279         retval = radix_delete(&pm->pm_tree, page->pg_index);
280         spin_unlock(&pm->pm_tree_lock);
281         assert(retval == (void*)page);
282         page_decref(page);
283         page->pg_mapping = 0;
284         page->pg_index = 0;
285         pm->pm_num_pages--;
286         return 0;
287 }
288
289 /* Makes sure the index'th page from file is loaded in the page cache and
290  * returns its location via **pp.  Note this will give you a refcnt'd reference.
291  * This may block! TODO: (BLK) */
292 int file_load_page(struct file *file, unsigned long index, struct page **pp)
293 {
294         struct page_map *pm = file->f_mapping;
295         struct page *page;
296         int error;
297         bool page_was_mapped = TRUE;
298
299         page = pm_find_page(pm, index);
300         while (!page) {
301                 /* kpage_alloc, since we want the page to persist after the proc
302                  * dies (can be used by others, until the inode shuts down). */
303                 if (kpage_alloc(&page))
304                         return -ENOMEM;
305                 /* might want to initialize other things, perhaps in page_alloc() */
306                 page->pg_flags = 0;
307                 error = pm_insert_page(pm, index, page);
308                 switch (error) {
309                         case 0:
310                                 page_was_mapped = FALSE;
311                                 break;
312                         case -EEXIST:
313                                 /* the page was mapped already (benign race), just get rid of
314                                  * our page and try again (the only case that uses the while) */
315                                 page_decref(page);
316                                 page = pm_find_page(pm, index);
317                                 break;
318                         default:
319                                 /* something is wrong, bail out! */
320                                 page_decref(page);
321                                 return error;
322                 }
323         }
324         *pp = page;
325         /* if the page was in the map, we need to do some checks, and might have to
326          * read in the page later.  If the page was freshly inserted to the pm by
327          * us, we skip this since we are the one doing the readpage(). */
328         if (page_was_mapped) {
329                 /* is it already here and up to date?  if so, we're done */
330                 if (page->pg_flags & PG_UPTODATE)
331                         return 0;
332                 /* if not, try to lock the page (could BLOCK) */
333                 lock_page(page);
334                 /* we got it, is our page still in the cache?  check the mapping.  if
335                  * not, start over, perhaps with EAGAIN and outside support */
336                 if (!page->pg_mapping)
337                         panic("Page is not in the mapping!  Haven't implemented this!");
338                 /* double check, are we up to date?  if so, we're done */
339                 if (page->pg_flags & PG_UPTODATE) {
340                         unlock_page(page);
341                         return 0;
342                 }
343         }
344         /* if we're here, the page is locked by us, and it needs to be read in */
345         assert(page->pg_mapping == pm);
346         error = pm->pm_op->readpage(file, page);
347         assert(!error);
348         /* Try to sleep on the IO.  The page will be unlocked when the IO is done */
349         lock_page(page);
350         unlock_page(page);
351         assert(page->pg_flags & PG_UPTODATE);
352         return 0;
353 }