Converts page_map for bdev usage
[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_size = 512;
34         ram_bd->b_num_sectors = (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. */
82 int make_request(struct block_device *bdev, struct block_request *req)
83 {
84         void *src, *dst;
85         /* Sectors are indexed starting with 0, for now. */
86         if (req->first_sector + req->amount > bdev->b_num_sectors)
87                 return -1;
88         if (req->flags & BREQ_READ) {
89                 dst = req->buffer;
90                 src = bdev->b_data + (req->first_sector << SECTOR_SZ_LOG);
91         } else if (req->flags & BREQ_WRITE) {
92                 dst = bdev->b_data + (req->first_sector << SECTOR_SZ_LOG);
93                 src = req->buffer;
94         } else {
95                 panic("Need a request type!\n");
96         }
97         memcpy(dst, src, req->amount << SECTOR_SZ_LOG);
98         return 0;
99 }
100
101 /* Fills page with the appropriate data from the block device.  There is only
102  * one BH, since bdev pages must be made of contiguous blocks.  Ideally, this
103  * will work for a bdev file that reads the pm via generic_file_read(), though
104  * that is a ways away still. */
105 int block_readpage(struct page_map *pm, struct page *page)
106 {
107         int retval;
108         unsigned long first_byte = page->pg_index * PGSIZE;
109         struct block_device *bdev = pm->pm_bdev;
110         struct block_request *breq = kmem_cache_alloc(breq_kcache, 0);
111         struct buffer_head *bh = kmem_cache_alloc(bh_kcache, 0);
112
113         assert(breq && bh);             /* should unlock_page and return -1 */
114         assert(pm == page->pg_mapping);
115
116         /* TODO: move this BH stuff to a helper.  Check for existing BH.  Check for
117          * exceeding the size, sort out block size */
118
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_blocknum = page->pg_index * PGSIZE / bdev->b_sector_size;
126         bh->bh_numblock = PGSIZE / bdev->b_sector_size;
127         page->pg_private = bh;
128
129         /* Build the request.  TODO: think about the overlap btw this and the BH */
130         breq->flags = BREQ_READ;
131         breq->buffer = page2kva(page);
132         breq->first_sector = first_byte >> SECTOR_SZ_LOG;
133         breq->amount = PGSIZE >> SECTOR_SZ_LOG; // TODO: change amount to num_sect
134         retval = make_request(bdev, breq);                               /* This blocks */
135         assert(!retval);
136
137         /* after the data is read, we mark it up to date and unlock the page: */
138         page->pg_flags |= PG_UPTODATE;
139         unlock_page(page);
140         kmem_cache_free(breq_kcache, breq);
141         return 0;
142 }
143
144 /* Block device page map ops: */
145 struct page_map_operations block_pm_op = {
146         block_readpage,
147 };
148
149 /* Block device file ops: for now, we don't let you do much of anything */
150 struct file_operations block_f_op = {
151         dev_c_llseek,
152         0,
153         0,
154         kfs_readdir,    /* this will fail gracefully */
155         dev_mmap,
156         kfs_open,
157         kfs_flush,
158         kfs_release,
159         0,      /* fsync - makes no sense */
160         kfs_poll,
161         0,      /* readv */
162         0,      /* writev */
163         kfs_sendpage,
164         kfs_check_flags,
165 };