Constants needed for printing.
[akaros.git] / kern / src / blockdev.c
index b050f77..9028110 100644 (file)
@@ -10,6 +10,9 @@
 #include <slab.h>
 #include <page_alloc.h>
 #include <pmap.h>
+/* These two are needed for the fake interrupt */
+#include <alarm.h>
+#include <smp.h>
 
 struct file_operations block_f_op;
 struct page_map_operations block_pm_op;
@@ -22,7 +25,7 @@ void block_init(void)
        bh_kcache = kmem_cache_create("buffer_heads", sizeof(struct buffer_head),
                                      __alignof__(struct buffer_head), 0, 0, 0);
 
-       #ifdef __CONFIG_EXT2FS__
+       #ifdef CONFIG_EXT2FS
        /* Now probe for and init the block device for the ext2 ram disk */
        extern uint8_t _binary_mnt_ext2fs_img_size[];
        extern uint8_t _binary_mnt_ext2fs_img_start[];
@@ -31,7 +34,7 @@ void block_init(void)
        memset(ram_bd, 0, sizeof(struct block_device));
        ram_bd->b_id = 31337;
        ram_bd->b_sector_sz = 512;
-       ram_bd->b_nr_sector = (unsigned int)_binary_mnt_ext2fs_img_size / 512;
+       ram_bd->b_nr_sector = (unsigned long)_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;
@@ -44,7 +47,7 @@ void block_init(void)
        ram_bf->f_dentry->d_inode->i_mapping = &ram_bd->b_pm;
        ram_bf->f_dentry->d_inode->i_bdev = ram_bd;     /* this holds the bd kref */
        kref_put(&ram_bf->f_kref);
-       #endif /* __CONFIG_EXT2FS__ */
+       #endif /* CONFIG_EXT2FS */
 }
 
 /* Generic helper, returns a kref'd reference out of principle. */
@@ -80,41 +83,80 @@ 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 (RAM based block devs). */
-int make_request(struct block_device *bdev, struct block_request *req)
+int bdev_submit_request(struct block_device *bdev, struct block_request *breq)
 {
        void *src, *dst;
        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;
+       for (int i = 0; i < breq->nr_bhs; i++) {
+               first_sector = breq->bhs[i]->bh_sector;
+               nr_sector = breq->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;
+               if (breq->flags & BREQ_READ) {
+                       dst = breq->bhs[i]->bh_buffer;
                        src = bdev->b_data + (first_sector << SECTOR_SZ_LOG);
-               } else if (req->flags & BREQ_WRITE) {
+               } else if (breq->flags & BREQ_WRITE) {
                        dst = bdev->b_data + (first_sector << SECTOR_SZ_LOG);
-                       src = req->bhs[i]->bh_buffer;
+                       src = breq->bhs[i]->bh_buffer;
                } else {
                        panic("Need a request type!\n");
                }
                memcpy(dst, src, nr_sector << SECTOR_SZ_LOG);
        }
+       /* Faking the device interrupt with an alarm */
+       void breq_handler(struct alarm_waiter *waiter)
+       {
+               /* In the future, we'll need to figure out which breq this was in
+                * response to */
+               struct block_request *breq = (struct block_request*)waiter->data;
+               if (breq->callback)
+                       breq->callback(breq);
+               kfree(waiter);
+       }
+       struct timer_chain *tchain = &per_cpu_info[core_id()].tchain;
+       struct alarm_waiter *waiter = kmalloc(sizeof(struct alarm_waiter), 0);
+       init_awaiter(waiter, breq_handler);
+       /* Stitch things up, so we know how to find things later */
+       waiter->data = breq;
+       /* Set for 5ms. */
+       set_awaiter_rel(waiter, 5000);
+       set_alarm(tchain, waiter);
        return 0;
 }
 
+/* Helper method, unblocks someone blocked on sleep_on_breq(). */
+void generic_breq_done(struct block_request *breq)
+{
+       int8_t irq_state = 0;
+       if (!sem_up_irqsave(&breq->sem, &irq_state)) {
+               /* This shouldn't happen anymore.  Let brho know if it does. */
+               warn("[kernel] no one waiting on breq %p", breq);
+       }
+}
+
+/* Helper, pairs with generic_breq_done().  Note we sleep here on a semaphore
+ * instead of faking it with an alarm.  Ideally, this code will be the same even
+ * for real block devices (that don't fake things with timer interrupts). */
+void sleep_on_breq(struct block_request *breq)
+{
+       int8_t irq_state = 0;
+       /* Since printk takes a while, this may make you lose the race */
+       printd("Sleeping on breq %p\n", breq);
+       assert(irq_is_enabled());
+       sem_down_irqsave(&breq->sem, &irq_state);
+}
+
 /* 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)
 {
        page->pg_flags |= PG_UPTODATE;
-       unlock_page(page);
        return 0;
 }
 
@@ -129,8 +171,8 @@ int block_readpage(struct page_map *pm, struct page *page)
  *
  * Also note we're a little inconsistent with the use of sector sizes in certain
  * files.  We'll sort it eventually. */
-struct buffer_head *get_buffer(struct block_device *bdev, unsigned long blk_num,
-                               unsigned int blk_sz)
+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;
@@ -140,8 +182,10 @@ struct buffer_head *get_buffer(struct block_device *bdev, unsigned long blk_num,
        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;
-       assert(blk_offset < PGSIZE);
        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)
@@ -175,7 +219,10 @@ retry:
        /* 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)) {
+       /* Normally, there'd be an ABA problem here, but we never actually remove
+        * bhs from the chain until the whole page gets cleaned up, which can't
+        * happen while we hold a reference to the page. */
+       if (!atomic_cas_ptr((void**)next_loc, bh, new)) {
                kmem_cache_free(bh_kcache, new);
                goto retry;
        }
@@ -197,12 +244,15 @@ found:
        breq = kmem_cache_alloc(breq_kcache, 0);
        assert(breq);
        breq->flags = BREQ_READ;
+       breq->callback = generic_breq_done;
+       breq->data = 0;
+       sem_init_irqsave(&breq->sem, 0);
        breq->bhs = breq->local_bhs;
        breq->bhs[0] = bh;
        breq->nr_bhs = 1;
-       error = make_request(bdev, breq);
-       /* TODO: (BLK) this assumes we slept til the request was done */
+       error = bdev_submit_request(bdev, breq);
        assert(!error);
+       sleep_on_breq(breq);
        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;
@@ -213,7 +263,7 @@ found:
 /* 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 dirty_buffer(struct buffer_head *bh)
+void bdev_dirty_buffer(struct buffer_head *bh)
 {
        struct page *page = bh->bh_page;
        /* TODO: race on flag modification */
@@ -221,10 +271,10 @@ void dirty_buffer(struct buffer_head *bh)
        page->pg_flags |= PG_DIRTY;
 }
 
-/* Decrefs the buffer from get_buffer().  Call this when you no longer reference
- * your block/buffer.  For now, we do refcnting on the page, since the
+/* 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 put_buffer(struct buffer_head *bh)
+void bdev_put_buffer(struct buffer_head *bh)
 {
        page_decref(bh->bh_page);
 }