Kernel context (IRQ, etc) tracking
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 14 Nov 2012 23:29:02 +0000 (15:29 -0800)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 21 Nov 2012 23:41:17 +0000 (15:41 -0800)
We can now detect what sort of context we are in.  The main use for this
is deadlock detection.

RISC/SPARC people, take a look at your architectures please.

kern/arch/i686/trap.c
kern/arch/riscv/trap.c
kern/arch/sparc/trap.c
kern/include/smp.h
kern/include/trap.h
kern/src/smp.c
kern/src/trap.c

index cd3c3ce..7bb6a83 100644 (file)
@@ -327,6 +327,8 @@ void trap(struct trapframe *tf)
        /* Copy out the TF for now */
        if (!in_kernel(tf))
                set_current_tf(pcpui, tf);
+       else
+               inc_ktrap_depth(pcpui);
 
        printd("Incoming TRAP %d on core %d, TF at %p\n", tf->tf_trapno, core_id(),
               tf);
@@ -338,8 +340,10 @@ void trap(struct trapframe *tf)
        /* Return to the current process, which should be runnable.  If we're the
         * kernel, we should just return naturally.  Note that current and tf need
         * to still be okay (might not be after blocking) */
-       if (in_kernel(tf))
-               return; /* TODO: think about this, might want a helper instead. */
+       if (in_kernel(tf)) {
+               dec_ktrap_depth(pcpui);
+               return;
+       }
        proc_restartcore();
        assert(0);
 }
@@ -427,6 +431,7 @@ void irq_handler(struct trapframe *tf)
        /* Copy out the TF for now */
        if (!in_kernel(tf))
                set_current_tf(pcpui, tf);
+       inc_irq_depth(pcpui);
        /* Coupled with cpu_halt() and smp_idle() */
        abort_halt(tf);
        //if (core_id())
@@ -450,11 +455,12 @@ void irq_handler(struct trapframe *tf)
                down_checklist(handler_wrappers[tf->tf_trapno & 0x0f].cpu_list);
        /* Fall-through */
 out_no_eoi:
+       dec_irq_depth(pcpui);
        /* Return to the current process, which should be runnable.  If we're the
         * kernel, we should just return naturally.  Note that current and tf need
         * to still be okay (might not be after blocking) */
        if (in_kernel(tf))
-               return; /* TODO: think about this, might want a helper instead. */
+               return;
        proc_restartcore();
        assert(0);
 }
@@ -505,16 +511,13 @@ void sysenter_init(void)
 void sysenter_callwrapper(struct trapframe *tf)
 {
        struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
-       /* Copy out the TF for now */
-       if (!in_kernel(tf))
-               set_current_tf(pcpui, tf);
+       assert(!in_kernel(tf));
+       set_current_tf(pcpui, tf);
        /* Once we've set_current_tf, we can enable interrupts.  This used to be
         * mandatory (we had immediate KMSGs that would muck with cur_tf).  Now it
         * should only help for sanity/debugging. */
        enable_irq();
 
-       if (in_kernel(tf))
-               panic("sysenter from a kernel TF!!");
        /* Set up and run the async calls */
        prep_syscalls(current, (struct syscall*)tf->tf_regs.reg_eax,
                      tf->tf_regs.reg_esi);
index ce83949..59dfcdb 100644 (file)
@@ -297,18 +297,27 @@ handle_trap(trapframe_t* tf)
          [IRQ_IPI] = handle_ipi,
        };
        
+       struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
        if (tf->cause < 0)
        {
                uint8_t irq = tf->cause;
                assert(irq < sizeof(irq_handlers)/sizeof(irq_handlers[0]) &&
                       irq_handlers[irq]);
+               inc_irq_depth(pcpui);
                irq_handlers[irq](tf);
+               dec_irq_depth(pcpui);
        }
        else
        {
                assert(tf->cause < sizeof(trap_handlers)/sizeof(trap_handlers[0]) &&
                       trap_handlers[tf->cause]);
-               trap_handlers[tf->cause](tf);
+               if (in_kernel(tf)) {
+                       inc_ktrap_depth(pcpui);
+                       trap_handlers[tf->cause](tf);
+                       dec_ktrap_depth(pcpui);
+               } else {
+                       trap_handlers[tf->cause](tf);
+               }
        }
        
        /* Return to the current process, which should be runnable.  If we're the
index 3db0dac..6f4fe11 100644 (file)
@@ -22,6 +22,8 @@
 #pragma nodeputy
 #endif
 
+/* Warning: SPARC's trap handlers do not increment the ktrap depth */
+
 /* These are the stacks the kernel will load when it receives a trap from user
  * space.  The deal is that they get set right away in entry.S, and can always
  * be used for finding the top of the stack (from which you should subtract the
@@ -183,7 +185,9 @@ void handle_ipi(trapframe_t* tf)
        else if((void*)tf->pc == &__cpu_halt) // break out of the __cpu_halt loop
                advance_pc(tf);
 
+       inc_irq_depth(pcpui);
        handle_kmsg_ipi(tf, 0);
+       dec_irq_depth(pcpui);
 }
 
 void
index 4b444f4..6a2b52c 100644 (file)
@@ -31,6 +31,7 @@ struct per_cpu_info {
        uint32_t owning_vcoreid;        /* vcoreid of owning proc (if applicable */
        struct trapframe *cur_tf;       /* user tf we came in on (can be 0) */
        struct trapframe actual_tf;     /* storage for cur_tf */
+       uint32_t __ctx_depth;           /* don't access directly.  see trap.h. */
        struct syscall *cur_sysc;       /* ptr is into cur_proc's address space */
        struct kthread *spare;          /* useful when restarting */
        struct timer_chain tchain;      /* for the per-core alarm */
index 9edc7fd..11d8cb7 100644 (file)
@@ -100,4 +100,113 @@ void handle_kmsg_ipi(struct trapframe *tf, void *data);
 void process_routine_kmsg(void);
 void print_kmsgs(uint32_t coreid);
 
+/* Kernel context depths.  IRQ depth is how many nested IRQ stacks/contexts we
+ * are working on.  Kernel trap depth is how many nested kernel traps (not
+ * user-space traps) we have.
+ *
+ * Some examples:
+ *             (original context in parens, +(x, y) is the change to IRQ and ktrap
+ *             depth):
+ * - syscall (user): +(0, 0)
+ * - trap (user): +(0, 0)
+ * - irq (user): +(1, 0)
+ * - irq (kernel, handling syscall): +(1, 0)
+ * - trap (kernel, regardless of context): +(0, 1)
+ * - NMI (kernel): it's actually a kernel trap, even though it is
+ *   sent by IPI.  +(0, 1)
+ * - NMI (user): just a trap.  +(0, 0)
+ *
+ * So if the user traps in for a syscall (0, 0), then the kernel takes an IRQ
+ * (1, 0), and then another IRQ (2, 0), and then the kernel page faults (a
+ * trap), we're at (2, 1).
+ *
+ * Or if we're in userspace, then an IRQ arrives, we're in the kernel at (1, 0).
+ * Note that regardless of whether or not we are in userspace or the kernel when
+ * an irq arrives, we still are only at level 1 irq depth.  We don't care if we
+ * have one or 0 kernel contexts under us.  (The reason for this is that I care
+ * if it is *possible* for us to interrupt the kernel, not whether or not it
+ * actually happened). */
+
+/* uint32_t __ctx_depth is laid out like so:
+ *
+ * +------8------+------8------+------8------+------8------+
+ * |    Flags    |    Unused   | Kernel Trap |  IRQ Depth  |
+ * |             |             |    Depth    |             |
+ * +-------------+-------------+-------------+-------------+
+ *
+ */
+#define __CTX_IRQ_D_SHIFT                      0
+#define __CTX_KTRAP_D_SHIFT                    8
+#define __CTX_FLAG_SHIFT                       24
+#define __CTX_IRQ_D_MASK                       ((1 << 8) - 1)
+#define __CTX_KTRAP_D_MASK                     ((1 << 8) - 1)
+#define __CTX_NESTED_CTX_MASK          ((1 << 16) - 1)
+#define __CTX_EARLY_RKM                        (1 << __CTX_FLAG_SHIFT)
+
+/* Basic functions to get or change depths */
+
+#define irq_depth(pcpui)                                                       \
+       (((pcpui)->__ctx_depth >> __CTX_IRQ_D_SHIFT) & __CTX_IRQ_D_MASK)
+
+#define ktrap_depth(pcpui)                                                     \
+       (((pcpui)->__ctx_depth >> __CTX_KTRAP_D_SHIFT) & __CTX_KTRAP_D_MASK)
+
+#define inc_irq_depth(pcpui)                                                   \
+       ((pcpui)->__ctx_depth += 1 << __CTX_IRQ_D_SHIFT)
+
+#define dec_irq_depth(pcpui)                                                   \
+       ((pcpui)->__ctx_depth -= 1 << __CTX_IRQ_D_SHIFT)
+
+#define inc_ktrap_depth(pcpui)                                                 \
+       ((pcpui)->__ctx_depth += 1 << __CTX_KTRAP_D_SHIFT)
+
+#define dec_ktrap_depth(pcpui)                                                 \
+       ((pcpui)->__ctx_depth -= 1 << __CTX_KTRAP_D_SHIFT)
+
+#define set_rkmsg(pcpui)                                                       \
+       ((pcpui)->__ctx_depth |= __CTX_EARLY_RKM)
+
+#define clear_rkmsg(pcpui)                                                     \
+       ((pcpui)->__ctx_depth &= ~__CTX_EARLY_RKM)
+
+/* Functions to query the kernel context depth/state.  I haven't fully decided
+ * on whether or not 'default' context includes RKMs or not.  Will depend on
+ * how we use it.  Check the code below to see what the latest is. */
+
+#define in_irq_ctx(pcpui)                                                      \
+       (irq_depth(pcpui))
+
+#define in_early_rkmsg_ctx(pcpui)                                              \
+       ((pcpui)->__ctx_depth & __CTX_EARLY_RKM)
+
+/* Right now, anything (KTRAP, IRQ, or RKM) makes us not 'default' */
+#define in_default_ctx(pcpui)                                                  \
+       (!(pcpui)->__ctx_depth)
+
+/* Can block only if we have no nested contexts (ktraps or irqs, (which are
+ * potentially nested contexts)) */
+#define can_block(pcpui)                                                       \
+       (!((pcpui)->__ctx_depth & __CTX_NESTED_CTX_MASK))
+
+/* TRUE if we are allowed to spin, given that the 'lock' was declared as not
+ * grabbable from IRQ context.  Meaning, we can't grab the lock from any nested
+ * context.  (And for most locks, we can never grab them from a kernel trap
+ * handler). 
+ *
+ * Example is a lock that is not declared as irqsave, but we later grab it from
+ * irq context.  This could deadlock the system, even if it doesn't do it this
+ * time.  This function will catch that. */
+#define can_spinwait_noirq(pcpui)                                              \
+       (!((pcpui)->__ctx_depth & __CTX_NESTED_CTX_MASK))
+
+/* TRUE if we are allowed to spin, given that the 'lock' was declared as
+ * potentially grabbable by IRQ context (such as with an irqsave lock).  We can
+ * never grab from a ktrap, since there is no way to prevent that.  And we must
+ * have IRQs disabled, since an IRQ handler could attempt to grab the lock. */
+#define can_spinwait_irq(pcpui)                                                \
+       ((!ktrap_depth(pcpui) && !irq_is_enabled()))
+
+/* Debugging */
+void print_kctx_depths(const char *str);
 #endif /* ROS_KERN_TRAP_H */
index d9feb93..0667a8d 100644 (file)
@@ -85,6 +85,8 @@ void smp_idle(void)
 void smp_percpu_init(void)
 {
        uint32_t coreid = core_id();
+       /* Don't initialize __ctx_depth here, since it is already 1 (at least on
+        * x86), since this runs in irq context. */
        /* Do this first */
        __arch_pcpu_init(coreid);
        per_cpu_info[coreid].spare = 0;
index 330c772..983311a 100644 (file)
@@ -192,3 +192,13 @@ void kmsg_queue_stat(void)
        }
 }
 
+void print_kctx_depths(const char *str)
+{
+       uint32_t coreid = core_id();
+       struct per_cpu_info *pcpui = &per_cpu_info[coreid];
+       
+       if (!str)
+               str = "(none)";
+       printk("%s: Core %d, irq depth %d, ktrap depth %d\n", str, coreid,
+              irq_depth(pcpui), ktrap_depth(pcpui));
+}