Cleaned up naming conventions in blockdev
[akaros.git] / kern / src / blockdev.c
index be12fe1..18ba977 100644 (file)
@@ -30,8 +30,8 @@ void block_init(void)
        struct block_device *ram_bd = kmalloc(sizeof(struct block_device), 0);
        memset(ram_bd, 0, sizeof(struct block_device));
        ram_bd->b_id = 31337;
-       ram_bd->b_sector_size = 512;
-       ram_bd->b_num_sectors = (unsigned int)_binary_mnt_ext2fs_img_size / 512;
+       ram_bd->b_sector_sz = 512;
+       ram_bd->b_nr_sector = (unsigned int)_binary_mnt_ext2fs_img_size / 512;
        kref_init(&ram_bd->b_kref, fake_release, 1);
        pm_init(&ram_bd->b_pm, &block_pm_op, ram_bd);
        ram_bd->b_data = _binary_mnt_ext2fs_img_start;
@@ -61,7 +61,8 @@ struct block_device *get_bdev(char *path)
 }
 
 /* Frees all the BHs associated with page.  There could be 0, to deal with one
- * that wasn't UPTODATE.  Don't call this on a page that isn't a PG_BUFFER */
+ * that wasn't UPTODATE.  Don't call this on a page that isn't a PG_BUFFER.
+ * Note, these are not a circular LL (for now). */
 void free_bhs(struct page *page)
 {
        struct buffer_head *bh, *next;
@@ -78,69 +79,158 @@ void free_bhs(struct page *page)
 
 /* This ultimately will handle the actual request processing, all the way down
  * to the driver, and will deal with blocking.  For now, we just fulfill the
- * request right away. */
-int make_request(struct block_device *bdev, struct block_request *req)
+ * request right away (RAM based block devs). */
+int bdev_submit_request(struct block_device *bdev, struct block_request *req)
 {
        void *src, *dst;
-       /* Sectors are indexed starting with 0, for now. */
-       if (req->first_sector + req->amount > bdev->b_num_sectors)
-               return -1;
-       if (req->flags & BREQ_READ) {
-               dst = req->buffer;
-               src = bdev->b_data + (req->first_sector << SECTOR_SZ_LOG);
-       } else if (req->flags & BREQ_WRITE) {
-               dst = bdev->b_data + (req->first_sector << SECTOR_SZ_LOG);
-               src = req->buffer;
-       } else {
-               panic("Need a request type!\n");
+       unsigned long first_sector;
+       unsigned int nr_sector;
+
+       for (int i = 0; i < req->nr_bhs; i++) {
+               first_sector = req->bhs[i]->bh_sector;
+               nr_sector = req->bhs[i]->bh_nr_sector;
+               /* Sectors are indexed starting with 0, for now. */
+               if (first_sector + nr_sector > bdev->b_nr_sector) {
+                       warn("Exceeding the num sectors!");
+                       return -1;
+               }
+               if (req->flags & BREQ_READ) {
+                       dst = req->bhs[i]->bh_buffer;
+                       src = bdev->b_data + (first_sector << SECTOR_SZ_LOG);
+               } else if (req->flags & BREQ_WRITE) {
+                       dst = bdev->b_data + (first_sector << SECTOR_SZ_LOG);
+                       src = req->bhs[i]->bh_buffer;
+               } else {
+                       panic("Need a request type!\n");
+               }
+               memcpy(dst, src, nr_sector << SECTOR_SZ_LOG);
        }
-       memcpy(dst, src, req->amount << SECTOR_SZ_LOG);
        return 0;
 }
 
-/* Fills page with the appropriate data from the block device.  There is only
- * one BH, since bdev pages must be made of contiguous blocks.  Ideally, this
- * will work for a bdev file that reads the pm via generic_file_read(), though
- * that is a ways away still. */
+/* This just tells the page cache that it is 'up to date'.  Due to the nature of
+ * the blocks in the page cache, we don't actually read the items in on
+ * readpage, we read them in when a specific block is there */
 int block_readpage(struct page_map *pm, struct page *page)
 {
-       int retval;
-       unsigned long first_byte = page->pg_index * PGSIZE;
-       struct block_device *bdev = pm->pm_bdev;
-       struct block_request *breq = kmem_cache_alloc(breq_kcache, 0);
-       struct buffer_head *bh = kmem_cache_alloc(bh_kcache, 0);
-
-       assert(breq && bh);             /* should unlock_page and return -1 */
-       assert(pm == page->pg_mapping);
-
-       /* TODO: move this BH stuff to a helper.  Check for existing BH.  Check for
-        * exceeding the size, sort out block size */
-
-       /* Set up the BH.  bdevs do a 1:1 BH to page mapping */
-       bh->bh_page = page;                                                             /* weak ref */
-       bh->bh_buffer = page2kva(page);
-       bh->bh_flags = 0;                                                               /* whatever... */
-       bh->bh_next = 0;                                                                /* only one BH needed */
-       bh->bh_bdev = bdev;                                                             /* uncounted ref */
-       bh->bh_blocknum = page->pg_index * PGSIZE / bdev->b_sector_size;
-       bh->bh_numblock = PGSIZE / bdev->b_sector_size;
-       page->pg_private = bh;
-
-       /* Build the request.  TODO: think about the overlap btw this and the BH */
-       breq->flags = BREQ_READ;
-       breq->buffer = page2kva(page);
-       breq->first_sector = first_byte >> SECTOR_SZ_LOG;
-       breq->amount = PGSIZE >> SECTOR_SZ_LOG; // TODO: change amount to num_sect
-       retval = make_request(bdev, breq);                               /* This blocks */
-       assert(!retval);
-
-       /* after the data is read, we mark it up to date and unlock the page: */
        page->pg_flags |= PG_UPTODATE;
        unlock_page(page);
-       kmem_cache_free(breq_kcache, breq);
        return 0;
 }
 
+/* Returns a BH pointing to the buffer where blk_num from bdev is located (given
+ * blocks of size blk_sz).  This uses the page cache for the page allocations
+ * and evictions, but only caches blocks that are requested.  Check the docs for
+ * more info.  The BH isn't refcounted, but a page refcnt is returned.  Call
+ * put_block (nand/xor dirty block).
+ *
+ * Note we're using the lock_page() to sync (which is what we do with the page
+ * cache too.  It's not ideal, but keeps things simpler for now.
+ *
+ * Also note we're a little inconsistent with the use of sector sizes in certain
+ * files.  We'll sort it eventually. */
+struct buffer_head *bdev_get_buffer(struct block_device *bdev,
+                                    unsigned long blk_num, unsigned int blk_sz)
+{
+       struct page *page;
+       struct page_map *pm = &bdev->b_pm;
+       struct buffer_head *bh, *new, *prev, **next_loc;
+       struct block_request *breq;
+       int error;
+       unsigned int blk_per_pg = PGSIZE / blk_sz;
+       unsigned int sct_per_blk = blk_sz / bdev->b_sector_sz;
+       unsigned int blk_offset = (blk_num % blk_per_pg) * blk_sz;
+       void *my_buf;
+       assert(blk_offset < PGSIZE);
+       if (!blk_num)
+               warn("Asking for the 0th block of a bdev...");
+       /* Make sure there's a page in the page cache.  Should always be one. */
+       error = pm_load_page(pm, blk_num / blk_per_pg, &page); 
+       if (error)
+               panic("Failed to load page! (%d)", error);
+       my_buf = page2kva(page) + blk_offset;
+       assert(page->pg_flags & PG_BUFFER);             /* Should be part of a page map */
+retry:
+       bh = (struct buffer_head*)page->pg_private;
+       prev = 0;
+       /* look through all the BHs for ours, stopping if we go too far. */
+       while (bh) {
+               if (bh->bh_buffer == my_buf) {
+                       goto found;
+               } else if (bh->bh_buffer > my_buf) {
+                       break;
+               }
+               prev = bh;
+               bh = bh->bh_next;
+       }
+       /* At this point, bh points to the one beyond our space (or 0), and prev is
+        * either the one before us or 0.  We make a BH, and try to insert */
+       new = kmem_cache_alloc(bh_kcache, 0);
+       assert(new);
+       new->bh_page = page;                                    /* weak ref */
+       new->bh_buffer = my_buf;
+       new->bh_flags = 0;
+       new->bh_next = bh;
+       new->bh_bdev = bdev;                                    /* uncounted ref */
+       new->bh_sector = blk_num * sct_per_blk;
+       new->bh_nr_sector = sct_per_blk;
+       /* Try to insert the new one in place.  If it fails, retry the whole "find
+        * the bh" process.  This should be rare, so no sense optimizing it. */
+       next_loc = prev ? &prev->bh_next : (struct buffer_head**)&page->pg_private;
+       if (!atomic_comp_swap((uint32_t*)next_loc, (uint32_t)bh, (uint32_t)new)) {
+               kmem_cache_free(bh_kcache, new);
+               goto retry;
+       }
+       bh = new;
+found:
+       /* At this point, we have the BH for our buf, but it might not be up to
+        * date, and there might be someone else trying to update it. */
+       /* is it already here and up to date?  if so, we're done */
+       if (bh->bh_flags & BH_UPTODATE)
+               return bh;
+       /* if not, try to lock the page (could BLOCK).  Using this for syncing. */
+       lock_page(page);
+       /* double check, are we up to date?  if so, we're done */
+       if (bh->bh_flags & BH_UPTODATE) {
+               unlock_page(page);
+               return bh;
+       }
+       /* if we're here, the page is locked by us, we need to read the block */
+       breq = kmem_cache_alloc(breq_kcache, 0);
+       assert(breq);
+       breq->flags = BREQ_READ;
+       breq->bhs = breq->local_bhs;
+       breq->bhs[0] = bh;
+       breq->nr_bhs = 1;
+       error = bdev_submit_request(bdev, breq);
+       /* TODO: (BLK) this assumes we slept til the request was done */
+       assert(!error);
+       kmem_cache_free(breq_kcache, breq);
+       /* after the data is read, we mark it up to date and unlock the page. */
+       bh->bh_flags |= BH_UPTODATE;
+       unlock_page(page);
+       return bh;
+}
+
+/* Will dirty the block/BH/page for the given block/buffer.  Will have to be
+ * careful with the page reclaimer - if someone holds a reference, they can
+ * still dirty it. */
+void bdev_dirty_buffer(struct buffer_head *bh)
+{
+       struct page *page = bh->bh_page;
+       /* TODO: race on flag modification */
+       bh->bh_flags |= BH_DIRTY;
+       page->pg_flags |= PG_DIRTY;
+}
+
+/* Decrefs the buffer from bdev_get_buffer().  Call this when you no longer
+ * reference your block/buffer.  For now, we do refcnting on the page, since the
+ * reclaiming will be in page sized chunks from the page cache. */
+void bdev_put_buffer(struct buffer_head *bh)
+{
+       page_decref(bh->bh_page);
+}
+
 /* Block device page map ops: */
 struct page_map_operations block_pm_op = {
        block_readpage,