Individually manages buffers in the buffer 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 /* This just tells the page cache that it is 'up to date'.  Due to the nature of
112  * the blocks in the page cache, we don't actually read the items in on
113  * readpage, we read them in when a specific block is there */
114 int block_readpage(struct page_map *pm, struct page *page)
115 {
116         page->pg_flags |= PG_UPTODATE;
117         unlock_page(page);
118         return 0;
119 }
120
121 /* Returns a BH pointing to the buffer where blk_num from bdev is located (given
122  * blocks of size blk_sz).  This uses the page cache for the page allocations
123  * and evictions, but only caches blocks that are requested.  Check the docs for
124  * more info.  The BH isn't refcounted, but a page refcnt is returned.  Call
125  * put_block (nand/xor dirty block).
126  *
127  * Note we're using the lock_page() to sync (which is what we do with the page
128  * cache too.  It's not ideal, but keeps things simpler for now.
129  *
130  * Also note we're a little inconsistent with the use of sector sizes in certain
131  * files.  We'll sort it eventually. */
132 struct buffer_head *get_buffer(struct block_device *bdev, unsigned long blk_num,
133                                unsigned int blk_sz)
134 {
135         struct page *page;
136         struct page_map *pm = &bdev->b_pm;
137         struct buffer_head *bh, *new, *prev, **next_loc;
138         struct block_request *breq;
139         int error;
140         unsigned int blk_per_pg = PGSIZE / blk_sz;
141         unsigned int sct_per_blk = blk_sz / bdev->b_sector_sz;
142         unsigned int blk_offset = (blk_num % blk_per_pg) * blk_sz;
143         assert(blk_offset < PGSIZE);
144         void *my_buf;
145         /* Make sure there's a page in the page cache.  Should always be one. */
146         error = pm_load_page(pm, blk_num / blk_per_pg, &page); 
147         if (error)
148                 panic("Failed to load page! (%d)", error);
149         my_buf = page2kva(page) + blk_offset;
150         assert(page->pg_flags & PG_BUFFER);             /* Should be part of a page map */
151 retry:
152         bh = (struct buffer_head*)page->pg_private;
153         prev = 0;
154         /* look through all the BHs for ours, stopping if we go too far. */
155         while (bh) {
156                 if (bh->bh_buffer == my_buf) {
157                         goto found;
158                 } else if (bh->bh_buffer > my_buf) {
159                         break;
160                 }
161                 prev = bh;
162                 bh = bh->bh_next;
163         }
164         /* At this point, bh points to the one beyond our space (or 0), and prev is
165          * either the one before us or 0.  We make a BH, and try to insert */
166         new = kmem_cache_alloc(bh_kcache, 0);
167         assert(new);
168         new->bh_page = page;                                    /* weak ref */
169         new->bh_buffer = my_buf;
170         new->bh_flags = 0;
171         new->bh_next = bh;
172         new->bh_bdev = bdev;                                    /* uncounted ref */
173         new->bh_sector = blk_num * sct_per_blk;
174         new->bh_nr_sector = sct_per_blk;
175         /* Try to insert the new one in place.  If it fails, retry the whole "find
176          * the bh" process.  This should be rare, so no sense optimizing it. */
177         next_loc = prev ? &prev->bh_next : (struct buffer_head**)&page->pg_private;
178         if (!atomic_comp_swap((uint32_t*)next_loc, (uint32_t)bh, (uint32_t)new)) {
179                 kmem_cache_free(bh_kcache, new);
180                 goto retry;
181         }
182         bh = new;
183 found:
184         /* At this point, we have the BH for our buf, but it might not be up to
185          * date, and there might be someone else trying to update it. */
186         /* is it already here and up to date?  if so, we're done */
187         if (bh->bh_flags & BH_UPTODATE)
188                 return bh;
189         /* if not, try to lock the page (could BLOCK).  Using this for syncing. */
190         lock_page(page);
191         /* double check, are we up to date?  if so, we're done */
192         if (bh->bh_flags & BH_UPTODATE) {
193                 unlock_page(page);
194                 return bh;
195         }
196         /* if we're here, the page is locked by us, we need to read the block */
197         breq = kmem_cache_alloc(breq_kcache, 0);
198         assert(breq);
199         breq->flags = BREQ_READ;
200         breq->bhs = breq->local_bhs;
201         breq->bhs[0] = bh;
202         breq->nr_bhs = 1;
203         error = make_request(bdev, breq);
204         /* TODO: (BLK) this assumes we slept til the request was done */
205         assert(!error);
206         kmem_cache_free(breq_kcache, breq);
207         /* after the data is read, we mark it up to date and unlock the page. */
208         bh->bh_flags |= BH_UPTODATE;
209         unlock_page(page);
210         return bh;
211 }
212
213 /* Will dirty the block/BH/page for the given block/buffer.  Will have to be
214  * careful with the page reclaimer - if someone holds a reference, they can
215  * still dirty it. */
216 void dirty_buffer(struct buffer_head *bh)
217 {
218         struct page *page = bh->bh_page;
219         /* TODO: race on flag modification */
220         bh->bh_flags |= BH_DIRTY;
221         page->pg_flags |= PG_DIRTY;
222 }
223
224 /* Decrefs the buffer from get_buffer().  Call this when you no longer reference
225  * your block/buffer.  For now, we do refcnting on the page, since the
226  * reclaiming will be in page sized chunks from the page cache. */
227 void put_buffer(struct buffer_head *bh)
228 {
229         page_decref(bh->bh_page);
230 }
231
232 /* Block device page map ops: */
233 struct page_map_operations block_pm_op = {
234         block_readpage,
235 };
236
237 /* Block device file ops: for now, we don't let you do much of anything */
238 struct file_operations block_f_op = {
239         dev_c_llseek,
240         0,
241         0,
242         kfs_readdir,    /* this will fail gracefully */
243         dev_mmap,
244         kfs_open,
245         kfs_flush,
246         kfs_release,
247         0,      /* fsync - makes no sense */
248         kfs_poll,
249         0,      /* readv */
250         0,      /* writev */
251         kfs_sendpage,
252         kfs_check_flags,
253 };