x86: Prepare removal of previous_esp from i386 thread_info structure
authorSteven Rostedt <srostedt@redhat.com>
Thu, 6 Feb 2014 14:41:30 +0000 (09:41 -0500)
committerH. Peter Anvin <hpa@linux.intel.com>
Fri, 7 Mar 2014 00:56:54 +0000 (16:56 -0800)
The i386 thread_info contains a previous_esp field that is used
to daisy chain the different stacks for dump_stack()
(ie. irq, softirq, thread stacks).

The goal is to eventual make i386 handling of thread_info the same
as x86_64, which means that the thread_info will not be in the stack
but as a per_cpu variable. We will no longer depend on thread_info
being able to daisy chain different stacks as it will only exist
in one location (the thread stack).

By moving previous_esp to the end of thread_info and referencing
it as an offset instead of using a thread_info field, this becomes
a stepping stone to moving the thread_info.

The offset to get to the previous stack is rather ugly in this
patch, but this is only temporary and the prev_esp will be changed
in the next commit. This commit is more for sanity checks of the
change.

Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Robert Richter <rric@kernel.org>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Link: http://lkml.kernel.org/r/20110806012353.891757693@goodmis.org
Link: http://lkml.kernel.org/r/20140206144321.608754481@goodmis.org
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
arch/x86/include/asm/thread_info.h
arch/x86/kernel/dumpstack_32.c
arch/x86/kernel/irq_32.c
arch/x86/kernel/ptrace.c

index 1cb3501636d27ed5d2d28f02e192f094c26101c7..ca2de1b48fac3692f8800f75ba8d21664d035892 100644 (file)
@@ -32,13 +32,14 @@ struct thread_info {
        mm_segment_t            addr_limit;
        struct restart_block    restart_block;
        void __user             *sysenter_return;
+       unsigned int            sig_on_uaccess_error:1;
+       unsigned int            uaccess_err:1;  /* uaccess failed */
 #ifdef CONFIG_X86_32
        unsigned long           previous_esp;   /* ESP of the previous stack in
                                                   case of nested (IRQ) stacks
+                                                  (Moved to end, to be removed soon)
                                                */
 #endif
-       unsigned int            sig_on_uaccess_error:1;
-       unsigned int            uaccess_err:1;  /* uaccess failed */
 };
 
 #define INIT_THREAD_INFO(tsk)                  \
index f2a1770ca176381b843f9ab6f08327e55a9a3d6c..187d6a749c191135339552c57d8828989c2eb3b1 100644 (file)
@@ -22,6 +22,7 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs,
                const struct stacktrace_ops *ops, void *data)
 {
        int graph = 0;
+       u32 *prev_esp;
 
        if (!task)
                task = current;
@@ -44,9 +45,17 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs,
                        ((unsigned long)stack & (~(THREAD_SIZE - 1)));
                bp = ops->walk_stack(context, stack, bp, ops, data, NULL, &graph);
 
-               stack = (unsigned long *)context->previous_esp;
+               /* Stop if not on irq stack */
+               if (task_stack_page(task) == context)
+                       break;
+
+               /* The previous esp is just above the context */
+               prev_esp = (u32 *) ((char *)context + sizeof(struct thread_info) -
+                                   sizeof(long));
+               stack = (unsigned long *)*prev_esp;
                if (!stack)
                        break;
+
                if (ops->stack(data, "IRQ") < 0)
                        break;
                touch_nmi_watchdog();
index d7fcbedc9c43fa9659b7f2f2c687b8402a826dd2..f135cc2ff3014ea2b4c34a5bfab8c78e26c1fa05 100644 (file)
@@ -81,7 +81,7 @@ static inline int
 execute_on_irq_stack(int overflow, struct irq_desc *desc, int irq)
 {
        union irq_ctx *curctx, *irqctx;
-       u32 *isp, arg1, arg2;
+       u32 *isp, *prev_esp, arg1, arg2;
 
        curctx = (union irq_ctx *) current_thread_info();
        irqctx = __this_cpu_read(hardirq_ctx);
@@ -98,7 +98,10 @@ execute_on_irq_stack(int overflow, struct irq_desc *desc, int irq)
        /* build the stack frame on the IRQ stack */
        isp = (u32 *) ((char *)irqctx + sizeof(*irqctx));
        irqctx->tinfo.task = curctx->tinfo.task;
-       irqctx->tinfo.previous_esp = current_stack_pointer;
+       /* Save the next esp after thread_info */
+       prev_esp = (u32 *) ((char *)irqctx + sizeof(struct thread_info) -
+                           sizeof(long));
+       *prev_esp = current_stack_pointer;
 
        if (unlikely(overflow))
                call_on_stack(print_stack_overflow, isp);
@@ -149,16 +152,20 @@ void do_softirq_own_stack(void)
 {
        struct thread_info *curctx;
        union irq_ctx *irqctx;
-       u32 *isp;
+       u32 *isp, *prev_esp;
 
        curctx = current_thread_info();
        irqctx = __this_cpu_read(softirq_ctx);
        irqctx->tinfo.task = curctx->task;
-       irqctx->tinfo.previous_esp = current_stack_pointer;
 
        /* build the stack frame on the softirq stack */
        isp = (u32 *) ((char *)irqctx + sizeof(*irqctx));
 
+       /* Push the previous esp onto the stack */
+       prev_esp = (u32 *) ((char *)irqctx + sizeof(struct thread_info) -
+                           sizeof(long));
+       *prev_esp = current_stack_pointer;
+
        call_on_stack(__do_softirq, isp);
 }
 
index 7461f50d5bb1e15dbf39d7f43a758138b0fad019..f352a7cc43a101e24e2913641d4cf551fbc2b90a 100644 (file)
@@ -184,14 +184,14 @@ unsigned long kernel_stack_pointer(struct pt_regs *regs)
 {
        unsigned long context = (unsigned long)regs & ~(THREAD_SIZE - 1);
        unsigned long sp = (unsigned long)&regs->sp;
-       struct thread_info *tinfo;
+       u32 *prev_esp;
 
        if (context == (sp & ~(THREAD_SIZE - 1)))
                return sp;
 
-       tinfo = (struct thread_info *)context;
-       if (tinfo->previous_esp)
-               return tinfo->previous_esp;
+       prev_esp = (u32 *)(context + sizeof(struct thread_info) - sizeof(long));
+       if (prev_esp)
+               return (unsigned long)prev_esp;
 
        return (unsigned long)regs;
 }