Ext2: can read files from the page cache
[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  * Note, these are not a circular LL (for now). */
66 void free_bhs(struct page *page)
67 {
68         struct buffer_head *bh, *next;
69         assert(page->pg_flags & PG_BUFFER);
70         bh = (struct buffer_head*)page->pg_private;
71         while (bh) {
72                 next = bh->bh_next;
73                 bh->bh_next = 0;
74                 kmem_cache_free(bh_kcache, bh);
75                 bh = next;
76         }
77         page->pg_private = 0;           /* catch bugs */
78 }
79
80 /* This ultimately will handle the actual request processing, all the way down
81  * to the driver, and will deal with blocking.  For now, we just fulfill the
82  * request right away (RAM based block devs). */
83 int make_request(struct block_device *bdev, struct block_request *req)
84 {
85         void *src, *dst;
86         unsigned long first_sector;
87         unsigned int nr_sector;
88
89         for (int i = 0; i < req->nr_bhs; i++) {
90                 first_sector = req->bhs[i]->bh_sector;
91                 nr_sector = req->bhs[i]->bh_nr_sector;
92                 /* Sectors are indexed starting with 0, for now. */
93                 if (first_sector + nr_sector > bdev->b_nr_sector) {
94                         warn("Exceeding the num sectors!");
95                         return -1;
96                 }
97                 if (req->flags & BREQ_READ) {
98                         dst = req->bhs[i]->bh_buffer;
99                         src = bdev->b_data + (first_sector << SECTOR_SZ_LOG);
100                 } else if (req->flags & BREQ_WRITE) {
101                         dst = bdev->b_data + (first_sector << SECTOR_SZ_LOG);
102                         src = req->bhs[i]->bh_buffer;
103                 } else {
104                         panic("Need a request type!\n");
105                 }
106                 memcpy(dst, src, nr_sector << SECTOR_SZ_LOG);
107         }
108         return 0;
109 }
110
111 /* Sets up the bidirectional mapping between the page and its buffer heads.
112  * Note that for a block device, this is 1:1.  We have the helper in the off
113  * chance we want to decouple the mapping from readpage (which we may want to
114  * do for writepage, esp on FSs), and to keep readpage simple. */
115 static int block_mappage(struct block_device *bdev, struct page *page)
116 {
117         struct buffer_head *bh;
118         assert(!page->pg_private);              /* double check that we aren't bh-mapped */
119         bh = kmem_cache_alloc(bh_kcache, 0);
120         if (!bh)
121                 return -ENOMEM;
122         /* Set up the BH.  bdevs do a 1:1 BH to page mapping */
123         bh->bh_page = page;                                                             /* weak ref */
124         bh->bh_buffer = page2kva(page);
125         bh->bh_flags = 0;                                                               /* whatever... */
126         bh->bh_next = 0;                                                                /* only one BH needed */
127         bh->bh_bdev = bdev;                                                             /* uncounted ref */
128         bh->bh_sector = page->pg_index * PGSIZE / bdev->b_sector_sz;
129         bh->bh_nr_sector = PGSIZE / bdev->b_sector_sz;
130         page->pg_private = bh;
131         return 0;
132 }
133
134 /* Fills page with the appropriate data from the block device.  There is only
135  * one BH, since bdev pages must be made of contiguous blocks.  Ideally, this
136  * will work for a bdev file that reads the pm via generic_file_read(), though
137  * that is a ways away still.  Techincally, you could get an ENOMEM for breq and
138  * have this called again with a BH already set up, but I want to see it (hence
139  * the assert). */
140 int block_readpage(struct page_map *pm, struct page *page)
141 {
142         int retval;
143         unsigned long first_byte = page->pg_index * PGSIZE;
144         struct block_device *bdev = pm->pm_bdev;
145         struct block_request *breq;
146
147         assert(pm == page->pg_mapping);         /* Should be part of a page map */
148         assert(page->pg_flags & PG_BUFFER);     /* Should be part of a page map */
149         assert(!page->pg_private);                      /* Should be no BH for this page yet */
150         /* Don't read beyond the device.  There might be an issue when using
151          * generic_file_read() with this readpage(). */
152         if (first_byte + PGSIZE > bdev->b_nr_sector * bdev->b_sector_sz) {
153                 unlock_page(page);
154                 return -EFBIG;
155         }
156         retval = block_mappage(bdev, page);
157         if (retval) {
158                 unlock_page(page);
159                 return retval;
160         }
161         /* Build and submit the request */
162         breq = kmem_cache_alloc(breq_kcache, 0);
163         if (!breq) {
164                 unlock_page(page);
165                 return -ENOMEM;
166         }
167         breq->flags = BREQ_READ;
168         breq->bhs = breq->local_bhs;
169         /* There's only one BH for a bdev page */
170         breq->bhs[0] = (struct buffer_head*)page->pg_private;
171         breq->nr_bhs = 1;
172         retval = make_request(bdev, breq);
173         /* TODO: (BLK) this assumes we slept til the request was done */
174         assert(!retval);
175         /* after the data is read, we mark it up to date and unlock the page. */
176         page->pg_flags |= PG_UPTODATE;
177         unlock_page(page);
178         kmem_cache_free(breq_kcache, breq);
179         return 0;
180 }
181
182 /* Block device page map ops: */
183 struct page_map_operations block_pm_op = {
184         block_readpage,
185 };
186
187 /* Block device file ops: for now, we don't let you do much of anything */
188 struct file_operations block_f_op = {
189         dev_c_llseek,
190         0,
191         0,
192         kfs_readdir,    /* this will fail gracefully */
193         dev_mmap,
194         kfs_open,
195         kfs_flush,
196         kfs_release,
197         0,      /* fsync - makes no sense */
198         kfs_poll,
199         0,      /* readv */
200         0,      /* writev */
201         kfs_sendpage,
202         kfs_check_flags,
203 };