Converts blockdev and sys_block() to use alarms
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 4 May 2011 00:11:17 +0000 (17:11 -0700)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:36:01 +0000 (17:36 -0700)
sys_block() uses the kthread-style alarm, and the blockdev fake
interrupt uses the handler-style alarm.  Now, we should be able to have
a bunch of alarms, whether blockdev or sys_block, on all the cores at
once.

kern/src/blockdev.c
kern/src/syscall.c
tests/block_test.c

index 7f4809a..336a7d9 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,27 +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)
        {
-               /* Turn off the interrupt, Re-register the old dumb handler */
-               set_core_timer(0);
-               register_interrupt_handler(interrupt_handlers,
-                                          LAPIC_TIMER_DEFAULT_VECTOR, timer_interrupt,
-                                          NULL);
                /* In the future, we'll need to figure out which breq this was in
                 * response to */
-               struct block_request *breq = (struct block_request*)data;
+               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.  or 50... TODO for some reason, KVM needs a much larger
-        * wait time so that we can get to sleep before the interrupt arrives.  Set
-        * this to 5000 for "real" use (this is just faking anyway...) */
-       set_core_timer(50000);
+       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.  The time might not be accurate in KVM. */
+       set_awaiter_rel(waiter, 5000);
+       set_alarm(tchain, waiter);
 #else
        if (breq->callback)
                breq->callback(breq);
@@ -140,7 +141,8 @@ void generic_breq_done(struct block_request *breq)
 #ifdef __i386__        /* Sparc can't restart kthreads yet */
        struct kthread *sleeper = __up_sem(&breq->sem);
        if (!sleeper) {
-               /* this is odd, but happened a lot with kvm */
+               /* this is odd, but happened a lot with kvm, probably due to the ghetto
+                * lack of a one-shot per-core timer.  keeping this just in case. */
                printk("[kernel] no one waiting on breq %08p\n", breq);
                return;
        }
@@ -151,13 +153,15 @@ void generic_breq_done(struct block_request *breq)
 #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)
 {
        /* 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 */
+#ifdef __i386__
        sleep_on(&breq->sem);
 #else
        /* Sparc can't block yet (TODO).  This only works if the completion happened
index d565453..07d5411 100644 (file)
@@ -95,46 +95,20 @@ static int sys_null(void)
 }
 
 /* Diagnostic function: blocks the kthread/syscall, to help userspace test its
- * async I/O handling.  Don't mix this with things that mess with the interrupt
- * handler, like other sys_blocks or the current blockdev crap. */
+ * async I/O handling. */
 static int sys_block(void)
 {
-       struct semaphore local_sem, *sem = &local_sem;
-       init_sem(sem, 0);
-#ifdef __i386__        /* Sparc can't register interrupt handlers yet */
-       /* Faking an interrupt.  The handler runs in interrupt context btw */
-       void x86_unblock_handler(struct trapframe *tf, void *data)
-       {
-               /* Turn off the interrupt, Re-register the old dumb handler */
-               set_core_timer(0);
-               register_interrupt_handler(interrupt_handlers,
-                                          LAPIC_TIMER_DEFAULT_VECTOR, timer_interrupt,
-                                          NULL);
-               struct semaphore *sem = (struct semaphore*)data;
-               struct kthread *sleeper = __up_sem(sem);
-               if (!sleeper) {
-                       warn("No one sleeping!");
-                       return;
-               }
-               kthread_runnable(sleeper);
-               assert(TAILQ_EMPTY(&sem->waiters));
-       }
-       void *prev_data = interrupt_handlers[LAPIC_TIMER_DEFAULT_VECTOR].data;
-       if (prev_data)
-               warn("Something (%08p) already waiting on the LAPIC timer!", prev_data);
-       register_interrupt_handler(interrupt_handlers, LAPIC_TIMER_DEFAULT_VECTOR,
-                                  x86_unblock_handler, sem);
-       /* This fakes a 1ms delay.  Though it might be less, esp in _M mode on KVM.
-        * TODO KVM-timing (adjust it up by a lot in a VM). */
-       set_core_timer(1000);   /* in microseconds */
+       struct timer_chain *tchain = &per_cpu_info[core_id()].tchain;
+       struct alarm_waiter a_waiter;
+       init_awaiter(&a_waiter, 0);
+       /* Block for 5ms.  The time might not be accurate in KVM.  Note printing
+        * takes a few ms, so your printds won't be perfect. */
        printd("[kernel] sys_block(), sleeping at %llu\n", read_tsc());
-       sleep_on(sem);
+       set_awaiter_rel(&a_waiter, 5000);
+       set_alarm(tchain, &a_waiter);
+       sleep_on_awaiter(&a_waiter);
        printd("[kernel] sys_block(), waking up at %llu\n", read_tsc());
        return 0;
-#else /* sparc */
-       set_errno(ENOSYS);
-       return -1;
-#endif
 }
 
 // Writes 'val' to 'num_writes' entries of the well-known array in the kernel
index 1ed7a54..ab132fd 100644 (file)
@@ -11,11 +11,8 @@ pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
        printf(__VA_ARGS__); \
        pthread_mutex_unlock(&lock);
 
-pthread_t t1;
-pthread_t t2;
-pthread_t t3;
-
-#define NUM_TEST_THREADS 1000
+#define NUM_TEST_THREADS 1
+#define NUM_TEST_LOOPS 1000
 
 pthread_t my_threads[NUM_TEST_THREADS];
 void *my_retvals[NUM_TEST_THREADS];
@@ -24,62 +21,24 @@ __thread int my_id;
 void *block_thread(void* arg)
 {      
        assert(!in_vcore_context());
-       for (int i = 0; i < 1000; i++) {
+       for (int i = 0; i < NUM_TEST_LOOPS; i++) {
                printf_safe("[A] pthread %d on vcore %d\n", pthread_self()->id, vcore_id());
                ros_syscall(SYS_block, 0, 0, 0, 0, 0, 0);
        }
        return (void*)(pthread_self()->id);
 }
 
-void *hello_thread(void* arg)
-{      
-       printf_safe("[A] pthread %d on vcore %d\n", pthread_self()->id, vcore_id());
-       return (void*)(pthread_self()->id);
-}
-
 int main(int argc, char** argv) 
 {
-       void *retval1 = 0;
-       void *retval2 = 0;
-       void *retval3 = 0;
-
-       /* yield test */
-       printf_safe("[A] About to create thread 1\n");
-       pthread_create(&t1, NULL, &block_thread, NULL);
-       #if 0
-       /* sys_block can't handle more than one at a time yet */
-       printf_safe("[A] About to create thread 2\n");
-       pthread_create(&t2, NULL, &block_thread, NULL);
-       printf_safe("[A] About to create thread 3\n");
-       pthread_create(&t3, NULL, &block_thread, NULL);
-       #endif
-       /* join on them */
-       printf_safe("[A] About to join on thread 1\n");
-       pthread_join(t1, &retval1);
-       printf_safe("[A] Successfully joined on thread 1 (retval: %p)\n", retval1);
-       #if 0
-       printf_safe("[A] About to join on thread 2\n");
-       pthread_join(t2, &retval2);
-       printf_safe("[A] Successfully joined on thread 2 (retval: %p)\n", retval2);
-       printf_safe("[A] About to join on thread 3\n");
-       pthread_join(t3, NULL);
-       printf_safe("[A] Successfully joined on thread 3 (retval: %p)\n", retval3);
-       #endif
-
-       #if 0
-       /* create and join on hellos */
-       while (1) {
-               for (int i = 1; i < NUM_TEST_THREADS; i++) {
-                       printf_safe("[A] About to create thread %d\n", i);
-                       pthread_create(&my_threads[i], NULL, &hello_thread, NULL);
-               }
-               for (int i = 1; i < NUM_TEST_THREADS; i++) {
-                       printf_safe("[A] About to join on thread %d\n", i);
-                       pthread_join(my_threads[i], &my_retvals[i]);
-                       printf_safe("[A] Successfully joined on thread %d (retval: %p)\n", i,
-                                   my_retvals[i]);
-               }
-               break;
+       for (int i = 0; i < NUM_TEST_THREADS; i++) {
+               printf_safe("[A] About to create thread %d\n", i);
+               pthread_create(&my_threads[i], NULL, &block_thread, NULL);
+       }
+       for (int i = 0; i < NUM_TEST_THREADS; i++) {
+               printf_safe("[A] About to join on thread %d\n", i);
+               pthread_join(my_threads[i], &my_retvals[i]);
+               printf_safe("[A] Successfully joined on thread %d (retval: %p)\n", i,
+                           my_retvals[i]);
        }
-       #endif
+       printf("All done, exiting cleanishly\n");
 }