Page map interface and munmap changes
[akaros.git] / kern / src / pagemap.c
1 /* Copyright (c) 2010 The Regents of the University of California
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * See LICENSE for details.
4  *
5  * Page mapping: maps an object (inode or block dev) in page size chunks.
6  * Analagous to Linux's "struct address space" */
7
8 #include <pmap.h>
9 #include <atomic.h>
10 #include <radix.h>
11 #include <kref.h>
12 #include <assert.h>
13 #include <stdio.h>
14
15 /* Initializes a PM.  Host should be an *inode or a *bdev (doesn't matter).  The
16  * reference this stores is uncounted. */
17 void pm_init(struct page_map *pm, struct page_map_operations *op, void *host)
18 {
19         pm->pm_bdev = host;                                             /* note the uncounted ref */
20         radix_tree_init(&pm->pm_tree);
21         spinlock_init(&pm->pm_tree_lock);
22         pm->pm_num_pages = 0;                                   /* no pages in a new pm */
23         pm->pm_op = op;
24         pm->pm_flags = 0;
25 }
26
27 /* Looks up the index'th page in the page map, returning an incref'd reference,
28  * or 0 if it was not in the map. */
29 static struct page *pm_find_page(struct page_map *pm, unsigned long index)
30 {
31         spin_lock(&pm->pm_tree_lock);
32         struct page *page = (struct page*)radix_lookup(&pm->pm_tree, index);
33         if (page)
34                 page_incref(page);
35         spin_unlock(&pm->pm_tree_lock);
36         return page;
37 }
38
39 /* Attempts to insert the page into the page_map, returns 0 for success, or an
40  * error code if there was one already (EEXIST) or we ran out of memory
41  * (ENOMEM).  On success, this will preemptively lock the page, and will also
42  * store a reference to the page in the pm. */
43 static int pm_insert_page(struct page_map *pm, unsigned long index,
44                           struct page *page)
45 {
46         int error = 0;
47         spin_lock(&pm->pm_tree_lock);
48         error = radix_insert(&pm->pm_tree, index, page);
49         if (!error) {
50                 page_incref(page);
51                 /* setting PG_BUF since we know it'll be used for IO later... */
52                 atomic_or(&page->pg_flags, PG_LOCKED | PG_BUFFER | PG_PAGEMAP);
53                 page->pg_sem.nr_signals = 0;            /* ensure others will block */
54                 page->pg_mapping = pm;
55                 page->pg_index = index;
56                 pm->pm_num_pages++;
57         }
58         spin_unlock(&pm->pm_tree_lock);
59         return error;
60 }
61
62 void pm_put_page(struct page *page)
63 {
64         page_decref(page);
65 }
66
67 /* Makes sure the index'th page of the mapped object is loaded in the page cache
68  * and returns its location via **pp.  Note this will give you a refcnt'd
69  * reference to the page.  This may block! TODO: (BLK) */
70 int pm_load_page(struct page_map *pm, unsigned long index, struct page **pp)
71 {
72         struct page *page;
73         int error;
74         bool page_was_mapped = TRUE;
75
76         page = pm_find_page(pm, index);
77         while (!page) {
78                 /* kpage_alloc, since we want the page to persist after the proc
79                  * dies (can be used by others, until the inode shuts down). */
80                 if (kpage_alloc(&page))
81                         return -ENOMEM;
82                 /* might want to initialize other things, perhaps in page_alloc() */
83                 atomic_set(&page->pg_flags, 0);
84                 error = pm_insert_page(pm, index, page);
85                 switch (error) {
86                         case 0:
87                                 page_was_mapped = FALSE;
88                                 break;
89                         case -EEXIST:
90                                 /* the page was mapped already (benign race), just get rid of
91                                  * our page and try again (the only case that uses the while) */
92                                 page_decref(page);
93                                 page = pm_find_page(pm, index);
94                                 break;
95                         default:
96                                 /* something is wrong, bail out! */
97                                 page_decref(page);
98                                 return error;
99                 }
100         }
101         assert(page && kref_refcnt(&page->pg_kref));
102         /* At this point, page is a refcnt'd page, and we return the reference.
103          * Also, there's an unlikely race where we're not in the page cache anymore,
104          * and this all is useless work. */
105         *pp = page;
106         /* if the page was in the map, we need to do some checks, and might have to
107          * read in the page later.  If the page was freshly inserted to the pm by
108          * us, we skip this since we are the one doing the readpage(). */
109         if (page_was_mapped) {
110                 /* is it already here and up to date?  if so, we're done */
111                 if (atomic_read(&page->pg_flags) & PG_UPTODATE)
112                         return 0;
113                 /* if not, try to lock the page (could BLOCK) */
114                 lock_page(page);
115                 /* we got it, is our page still in the cache?  check the mapping.  if
116                  * not, start over, perhaps with EAGAIN and outside support */
117                 if (!page->pg_mapping)
118                         panic("Page is not in the mapping!  Haven't implemented this!");
119                 /* double check, are we up to date?  if so, we're done */
120                 if (atomic_read(&page->pg_flags) & PG_UPTODATE) {
121                         unlock_page(page);
122                         return 0;
123                 }
124         }
125         /* if we're here, the page is locked by us, and it needs to be read in */
126         assert(page->pg_mapping == pm);
127         /* Readpage will block internally, returning when it is done */
128         error = pm->pm_op->readpage(pm, page);
129         assert(!error);
130         /* Unlock, since we're done with the page and it is up to date */
131         unlock_page(page);
132         assert(atomic_read(&page->pg_flags) & PG_UPTODATE);
133         return 0;
134 }