A patch to arch/i386/kernel/irq.c to help get backtraces of (near)stack overflow situations

From: John Hughes
Date: Fri Jan 16 2009 - 07:26:47 EST


I'm working on a system that is using a bit too much stack to work reliably with 4K stacks (OpenSSI with DRBD for the root filesystem replication) and I find that the CONFIG_DEBUG_STACKOVERFLOW stuff works rather poorly - often the stack dump will cause a stack overflow itself.

Here's a little patch to irq.c to make stack overflow dumps use a separate stack.

It's based on 2.6.12 'cos that's how out of date I am. :-)

Maybe it'll be of use to someone.

Please CC john@xxxxxxxxx with any comments, I'm not subscribed to the list. --- linux/arch/i386/kernel/irq.c 2005-06-17 21:48:29.000000000 +0200
+++ linux-ssi/arch/i386/kernel/irq.c 2009-01-15 13:09:45.000000000 +0100
@@ -41,6 +41,23 @@

static union irq_ctx *hardirq_ctx[NR_CPUS];
static union irq_ctx *softirq_ctx[NR_CPUS];
+#ifdef CONFIG_DEBUG_STACKOVERFLOW
+static union irq_ctx *overflow_ctx[NR_CPUS];
+#endif
+#ifdef CONFIG_KDB
+const char *kdba_irq_ctx_type(int cpu, struct thread_info *tinfo)
+{
+ if (tinfo == &hardirq_ctx[cpu]->tinfo)
+ return "hardirq_ctx";
+ if (tinfo == &softirq_ctx[cpu]->tinfo)
+ return "softirq_ctx";
+#ifdef CONFIG_DEBUG_STACKOVERFLOW
+ if (tinfo == &overflow_ctx[cpu]->tinfo)
+ return "overflow_ctx";
+#endif
+ return NULL;
+}
+#endif /* CONFIG_KDB */
#endif

/*
@@ -53,6 +70,7 @@
/* high bits used in ret_from_ code */
int irq = regs->orig_eax & 0xff;
#ifdef CONFIG_4KSTACKS
+ int arg1, arg2, ebx;
union irq_ctx *curctx, *irqctx;
u32 *isp;
#endif
@@ -66,9 +84,38 @@
__asm__ __volatile__("andl %%esp,%0" :
"=r" (esp) : "0" (THREAD_SIZE - 1));
if (unlikely(esp < (sizeof(struct thread_info) + STACK_WARN))) {
+#if CONFIG_4KSTACKS
+ /* use a special stack for overflow warning,
+ we want to avoid overflowing while telling
+ the user about the problem! */
+
+ curctx = (union irq_ctx *) current_thread_info();
+ irqctx = overflow_ctx[smp_processor_id()];
+
+ if (curctx == irqctx) {
+ panic("double overflow");
+ }
+
+ /* build the stack frame on the oflow stack */
+ isp = (u32*) ((char*)irqctx + sizeof(*irqctx));
+ irqctx->tinfo.task = curctx->tinfo.task;
+ irqctx->tinfo.previous_esp = current_stack_pointer;
+
+ asm volatile(
+ " xchgl %%ebx,%%esp \n"
+ " call __overflow \n"
+ " movl %%ebx,%%esp \n"
+ : "=a" (arg1), "=d" (arg2), "=b" (ebx)
+ : "0" (irq), "1" (esp), "2" (isp)
+ : "memory", "cc", "ecx"
+ );
+
+ printk ("Back from overflow\n");
+#else
printk("do_IRQ: stack overflow: %ld\n",
esp - sizeof(struct thread_info));
dump_stack();
+#endif
}
}
#endif
@@ -109,6 +156,17 @@
return 1;
}

+#ifdef CONFIG_DEBUG_STACKOVERFLOW
+#ifdef CONFIG_4KSTACKS
+static fastcall void __overflow (int irq, long esp)
+{
+ printk("do_IRQ: stack overflow: %ld\n",
+ esp - sizeof(struct thread_info));
+ dump_stack();
+}
+#endif
+#endif
+
#ifdef CONFIG_4KSTACKS

/*
@@ -121,6 +179,10 @@
static char hardirq_stack[NR_CPUS * THREAD_SIZE]
__attribute__((__aligned__(THREAD_SIZE)));

+#ifdef CONFIG_DEBUG_STACKOVERFLOW
+static char overflow_stack[NR_CPUS * THREAD_SIZE]
+ __attribute__((__aligned__(THREAD_SIZE)));
+#endif
/*
* allocate per-cpu stacks for hardirq and for softirq processing
*/
@@ -151,6 +213,20 @@

printk("CPU %u irqstacks, hard=%p soft=%p\n",
cpu,hardirq_ctx[cpu],softirq_ctx[cpu]);
+#ifdef CONFIG_DEBUG_STACKOVERFLOW
+
+ irqctx = (union irq_ctx*) &overflow_stack[cpu*THREAD_SIZE];
+ irqctx->tinfo.task = NULL;
+ irqctx->tinfo.exec_domain = NULL;
+ irqctx->tinfo.cpu = cpu;
+ irqctx->tinfo.preempt_count = HARDIRQ_OFFSET; /* ? */
+ irqctx->tinfo.addr_limit = MAKE_MM_SEG(0);
+
+ overflow_ctx[cpu] = irqctx;
+
+ printk("CPU %u irqstacks, overflow=%p\n",
+ cpu,overflow_ctx[cpu]);
+#endif
}

extern asmlinkage void __do_softirq(void);