QIO and catching rendez_sleep()
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 16 Apr 2014 01:31:44 +0000 (18:31 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 16 Apr 2014 01:36:04 +0000 (18:36 -0700)
rendez_sleep() throws an error.  In qbwrite(), this is after the point
where we published the block b to the queue.  If the rendez_sleep() is
aborted, we'd error out and freeb(b), which is still on the queue.

Our use of error() in rendez_sleep() might be different than Plan 9.  We
already have slightly different rendez semantics.  Though many rendez
calls are clearly wrapped in waserrors() in other places.

I looked at some of the other rendez spots, like devmnt, and at least
put some warnings in place for the next time we have potential rendez
issues.

kern/drivers/dev/mnt.c
kern/src/kthread.c
kern/src/ns/qio.c

index 1ee9ba7..55cccec 100644 (file)
@@ -808,6 +808,13 @@ void mountio(struct mnt *m, struct mntrpc *r)
        while (waserror()) {
                if (m->rip == current)
                        mntgate(m);
+               if (!strcmp(current_errstr(), "syscall aborted")) {
+                       /* not sure what devmnt wants us to do here.  bail on aborted
+                        * syscall?  keep looping forever? (probably not) */
+                       printk("[kernel] mountio had aborted syscall");
+                       mntflushfree(m, r);
+                       nexterror();
+               }
                if (strcmp(current_errstr(), Eintr) != 0) {
                        mntflushfree(m, r);
                        nexterror();
index df0e703..1436472 100644 (file)
@@ -181,16 +181,25 @@ void kthread_yield(void)
 
 static void __ktask_wrapper(uint32_t srcid, long a0, long a1, long a2)
 {
+       ERRSTACK(1);
        void (*fn)(void*) = (void (*)(void*))a0;
        void *arg = (void*)a1;
        char *name = (char*)a2;
        struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
        assert(pcpui->cur_kthread->is_ktask);
        pcpui->cur_kthread->name = name;
+       /* There are some rendezs out there that aren't wrapped.  Though no one can
+        * abort them.  Yet. */
+       if (waserror()) {
+               printk("Ktask %s threw error %s\n", name, current_errstr());
+               goto out;
+       }
        enable_irq();
        fn(arg);
+out:
        disable_irq();
        pcpui->cur_kthread->name = 0;
+       poperror();
        /* if we blocked, when we return, PRKM will smp_idle() */
 }
 
index 4f76e6f..d41ccf5 100644 (file)
@@ -833,10 +833,10 @@ static int notempty(void *a)
        return (q->state & Qclosed) || q->bfirst != 0;
 }
 
-/*
- *  wait for the queue to be non-empty or closed.
- *  called with q ilocked.
- */
+/* wait for the queue to be non-empty or closed.
+ *
+ * called with q ilocked.  rendez may error out, back through the caller, with
+ * the irqsave lock unlocked.  */
 static int qwait(struct queue *q)
 {
        /* wait for data */
@@ -854,6 +854,7 @@ static int qwait(struct queue *q)
 
                q->state |= Qstarve;    /* flag requesting producer to wake me */
                spin_unlock_irqsave(&q->lock);
+               /* may throw an error() */
                rendez_sleep(&q->rr, notempty, q);
                spin_lock_irqsave(&q->lock);
        }
@@ -1151,6 +1152,7 @@ long qbwrite(struct queue *q, struct block *b)
 {
        ERRSTACK(1);
        int n, dowakeup;
+       volatile bool should_free_b = TRUE;
 
        n = BLEN(b);
 
@@ -1162,7 +1164,7 @@ long qbwrite(struct queue *q, struct block *b)
        dowakeup = 0;
        qlock(&q->wlock);
        if (waserror()) {
-               if (b != NULL)
+               if (b != NULL && should_free_b)
                        freeb(b);
                qunlock(&q->wlock);
                nexterror();
@@ -1189,6 +1191,7 @@ long qbwrite(struct queue *q, struct block *b)
        }
 
        /* queue the block */
+       should_free_b = FALSE;
        if (q->bfirst)
                q->blast->next = b;
        else