Ext2 uses the page cache for block metadata
[akaros.git] / kern / src / blockdev.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  * Block devices and generic blockdev infrastructure */
6
7 #include <devfs.h>
8 #include <blockdev.h>
9 #include <kmalloc.h>
10 #include <slab.h>
11 #include <page_alloc.h>
12 #include <pmap.h>
13
14 struct file_operations block_f_op;
15 struct page_map_operations block_pm_op;
16 struct kmem_cache *breq_kcache;
17
18 void block_init(void)
19 {
20         breq_kcache = kmem_cache_create("block_reqs", sizeof(struct block_request),
21                                         __alignof__(struct block_request), 0, 0, 0);
22         bh_kcache = kmem_cache_create("buffer_heads", sizeof(struct buffer_head),
23                                       __alignof__(struct buffer_head), 0, 0, 0);
24
25         #ifdef __CONFIG_EXT2FS__
26         /* Now probe for and init the block device for the ext2 ram disk */
27         extern uint8_t _binary_mnt_ext2fs_img_size[];
28         extern uint8_t _binary_mnt_ext2fs_img_start[];
29         /* Build and init the block device */
30         struct block_device *ram_bd = kmalloc(sizeof(struct block_device), 0);
31         memset(ram_bd, 0, sizeof(struct block_device));
32         ram_bd->b_id = 31337;
33         ram_bd->b_sector_sz = 512;
34         ram_bd->b_nr_sector = (unsigned int)_binary_mnt_ext2fs_img_size / 512;
35         kref_init(&ram_bd->b_kref, fake_release, 1);
36         pm_init(&ram_bd->b_pm, &block_pm_op, ram_bd);
37         ram_bd->b_data = _binary_mnt_ext2fs_img_start;
38         strncpy(ram_bd->b_name, "RAMDISK", BDEV_INLINE_NAME);
39         ram_bd->b_name[BDEV_INLINE_NAME - 1] = '\0';
40         /* Connect it to the file system */
41         struct file *ram_bf = make_device("/dev/ramdisk", S_IRUSR | S_IWUSR,
42                                           __S_IFBLK, &block_f_op);
43         /* make sure the inode tracks the right pm (not it's internal one) */
44         ram_bf->f_dentry->d_inode->i_mapping = &ram_bd->b_pm;
45         ram_bf->f_dentry->d_inode->i_bdev = ram_bd;     /* this holds the bd kref */
46         kref_put(&ram_bf->f_kref);
47         #endif /* __CONFIG_EXT2FS__ */
48 }
49
50 /* Generic helper, returns a kref'd reference out of principle. */
51 struct block_device *get_bdev(char *path)
52 {
53         struct block_device *bdev;
54         struct file *block_f;
55         block_f = do_file_open(path, O_RDWR, 0);
56         assert(block_f);
57         bdev = block_f->f_dentry->d_inode->i_bdev;
58         kref_get(&bdev->b_kref, 1);
59         kref_put(&block_f->f_kref);
60         return bdev;
61 }
62
63 /* Frees all the BHs associated with page.  There could be 0, to deal with one
64  * that wasn't UPTODATE.  Don't call this on a page that isn't a PG_BUFFER */
65 void free_bhs(struct page *page)
66 {
67         struct buffer_head *bh, *next;
68         assert(page->pg_flags & PG_BUFFER);
69         bh = (struct buffer_head*)page->pg_private;
70         while (bh) {
71                 next = bh->bh_next;
72                 bh->bh_next = 0;
73                 kmem_cache_free(bh_kcache, bh);
74                 bh = next;
75         }
76         page->pg_private = 0;           /* catch bugs */
77 }
78
79 /* This ultimately will handle the actual request processing, all the way down
80  * to the driver, and will deal with blocking.  For now, we just fulfill the
81  * request right away (RAM based block devs). */
82 int make_request(struct block_device *bdev, struct block_request *req)
83 {
84         void *src, *dst;
85
86         /* Only handles one for now (TODO) */
87         assert(req->nr_bhs == 1);
88         unsigned long first_sector = req->bhs[0]->bh_sector;
89         unsigned int nr_sector = req->bhs[0]->bh_nr_sector;
90         /* Sectors are indexed starting with 0, for now. */
91         if (first_sector + nr_sector > bdev->b_nr_sector) {
92                 warn("Exceeding the num sectors!");
93                 return -1;
94         }
95         if (req->flags & BREQ_READ) {
96                 dst = req->bhs[0]->bh_buffer;
97                 src = bdev->b_data + (first_sector << SECTOR_SZ_LOG);
98         } else if (req->flags & BREQ_WRITE) {
99                 dst = bdev->b_data + (first_sector << SECTOR_SZ_LOG);
100                 src = req->bhs[0]->bh_buffer;
101         } else {
102                 panic("Need a request type!\n");
103         }
104         memcpy(dst, src, nr_sector << SECTOR_SZ_LOG);
105         return 0;
106 }
107
108 /* Sets up the bidirectional mapping between the page and its buffer heads.
109  * Note that for a block device, this is 1:1.  We have the helper in the off
110  * chance we want to decouple the mapping from readpage (which we may want to
111  * do for writepage, esp on FSs), and to keep readpage simple. */
112 static int block_mappage(struct block_device *bdev, struct page *page)
113 {
114         struct buffer_head *bh;
115         assert(!page->pg_private);              /* double check that we aren't bh-mapped */
116         bh = kmem_cache_alloc(bh_kcache, 0);
117         if (!bh)
118                 return -ENOMEM;
119         /* Set up the BH.  bdevs do a 1:1 BH to page mapping */
120         bh->bh_page = page;                                                             /* weak ref */
121         bh->bh_buffer = page2kva(page);
122         bh->bh_flags = 0;                                                               /* whatever... */
123         bh->bh_next = 0;                                                                /* only one BH needed */
124         bh->bh_bdev = bdev;                                                             /* uncounted ref */
125         bh->bh_sector = page->pg_index * PGSIZE / bdev->b_sector_sz;
126         bh->bh_nr_sector = PGSIZE / bdev->b_sector_sz;
127         page->pg_private = bh;
128         return 0;
129 }
130
131 /* Fills page with the appropriate data from the block device.  There is only
132  * one BH, since bdev pages must be made of contiguous blocks.  Ideally, this
133  * will work for a bdev file that reads the pm via generic_file_read(), though
134  * that is a ways away still.  Techincally, you could get an ENOMEM for breq and
135  * have this called again with a BH already set up, but I want to see it (hence
136  * the assert). */
137 int block_readpage(struct page_map *pm, struct page *page)
138 {
139         int retval;
140         unsigned long first_byte = page->pg_index * PGSIZE;
141         struct block_device *bdev = pm->pm_bdev;
142         struct block_request *breq;
143
144         assert(pm == page->pg_mapping);         /* Should be part of a page map */
145         assert(page->pg_flags & PG_BUFFER);     /* Should be part of a page map */
146         assert(!page->pg_private);                      /* Should be no BH for this page yet */
147         /* Don't read beyond the device.  There might be an issue when using
148          * generic_file_read() with this readpage(). */
149         if (first_byte + PGSIZE > bdev->b_nr_sector * bdev->b_sector_sz) {
150                 unlock_page(page);
151                 return -EFBIG;
152         }
153         retval = block_mappage(bdev, page);
154         if (retval) {
155                 unlock_page(page);
156                 return retval;
157         }
158         /* Build and submit the request */
159         breq = kmem_cache_alloc(breq_kcache, 0);
160         if (!breq) {
161                 unlock_page(page);
162                 return -ENOMEM;
163         }
164         breq->flags = BREQ_READ;
165         breq->bhs = breq->local_bhs;
166         /* There's only one BH for a bdev page */
167         breq->bhs[0] = (struct buffer_head*)page->pg_private;
168         breq->nr_bhs = 1;
169         retval = make_request(bdev, breq);
170         /* TODO: (BLK) this assumes we slept til the request was done */
171         assert(!retval);
172         /* after the data is read, we mark it up to date and unlock the page. */
173         page->pg_flags |= PG_UPTODATE;
174         unlock_page(page);
175         kmem_cache_free(breq_kcache, breq);
176         return 0;
177 }
178
179 /* Block device page map ops: */
180 struct page_map_operations block_pm_op = {
181         block_readpage,
182 };
183
184 /* Block device file ops: for now, we don't let you do much of anything */
185 struct file_operations block_f_op = {
186         dev_c_llseek,
187         0,
188         0,
189         kfs_readdir,    /* this will fail gracefully */
190         dev_mmap,
191         kfs_open,
192         kfs_flush,
193         kfs_release,
194         0,      /* fsync - makes no sense */
195         kfs_poll,
196         0,      /* readv */
197         0,      /* writev */
198         kfs_sendpage,
199         kfs_check_flags,
200 };