set_core_timer() now takes a periodic flag
[akaros.git] / kern / src / blockdev.c
index 5702b52..1e9b1ca 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;
@@ -105,22 +108,25 @@ int bdev_submit_request(struct block_device *bdev, struct block_request *breq)
                }
                memcpy(dst, src, nr_sector << SECTOR_SZ_LOG);
        }
-#ifdef __i386__        /* Sparc can't register interrupt handlers yet */
-       /* Faking an interrupt.  The handler runs in interrupt context btw */
-       void x86_breq_handler(struct trapframe *tf, void *data)
+#ifdef __i386__        /* Sparc can't kthread yet */
+       /* Faking the device interrupt with an alarm */
+       void breq_handler(struct alarm_waiter *waiter)
        {
-               /* Re-register the old dumb handler */
-               register_interrupt_handler(interrupt_handlers,
-                                          LAPIC_TIMER_DEFAULT_VECTOR, timer_interrupt,
-                                          NULL);
-               struct block_request *breq = (struct block_request*)data;
+               /* 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);
        }
-       register_interrupt_handler(interrupt_handlers, LAPIC_TIMER_DEFAULT_VECTOR,
-                                  x86_breq_handler, breq);
-       /* Fake a 5ms delay */
-       set_core_timer(5000);
+       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);
 #else
        if (breq->callback)
                breq->callback(breq);
@@ -132,20 +138,33 @@ int bdev_submit_request(struct block_device *bdev, struct block_request *breq)
 /* Helper method, unblocks someone blocked on sleep_on_breq(). */
 void generic_breq_done(struct block_request *breq)
 {
-       /* TODO: BLK - unblock the kthread sleeping on this request */
+#ifdef __i386__        /* Sparc can't restart kthreads yet */
+       struct kthread *sleeper = __up_sem(&breq->sem);
+       if (!sleeper) {
+               /* This shouldn't happen anymore.  Let brho know if it does. */
+               warn("[kernel] no one waiting on breq %08p", breq);
+               return;
+       }
+       kthread_runnable(sleeper);
+       assert(TAILQ_EMPTY(&breq->sem.waiters));
+#else
        breq->data = (void*)1;
+#endif
 }
 
-/* Helper, pairs with generic_breq_done() */
+/* 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)
 {
-       /* TODO: BLK Block til we are done: data gets toggled in the completion.
-        * This only works if the completion happened first (for now) */
+       /* Since printk takes a while, this may make you lose the race */
+       printd("Sleeping on breq %08p\n", breq);
        assert(irq_is_enabled());
-#ifdef __i386__        /* Sparc isn't interrupt driven yet */
-       while (!breq->data)
-               cpu_relax();
+#ifdef __i386__
+       sleep_on(&breq->sem);
 #else
+       /* Sparc can't block yet (TODO).  This only works if the completion happened
+        * first (for now) */
        assert(breq->data);
 #endif
 }
@@ -242,6 +261,7 @@ found:
        breq->flags = BREQ_READ;
        breq->callback = generic_breq_done;
        breq->data = 0;
+       init_sem(&breq->sem, 0);
        breq->bhs = breq->local_bhs;
        breq->bhs[0] = bh;
        breq->nr_bhs = 1;