diff options
-rw-r--r-- | arch/x86/kernel/dumpstack_64.c | 33 |
1 files changed, 32 insertions, 1 deletions
diff --git a/arch/x86/kernel/dumpstack_64.c b/arch/x86/kernel/dumpstack_64.c index a071e6be177e..004b8aa6a35f 100644 --- a/arch/x86/kernel/dumpstack_64.c +++ b/arch/x86/kernel/dumpstack_64.c | |||
@@ -101,6 +101,35 @@ static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack, | |||
101 | return NULL; | 101 | return NULL; |
102 | } | 102 | } |
103 | 103 | ||
104 | static inline int | ||
105 | in_irq_stack(unsigned long *stack, unsigned long *irq_stack, | ||
106 | unsigned long *irq_stack_end) | ||
107 | { | ||
108 | return (stack >= irq_stack && stack < irq_stack_end); | ||
109 | } | ||
110 | |||
111 | /* | ||
112 | * We are returning from the irq stack and go to the previous one. | ||
113 | * If the previous stack is also in the irq stack, then bp in the first | ||
114 | * frame of the irq stack points to the previous, interrupted one. | ||
115 | * Otherwise we have another level of indirection: We first save | ||
116 | * the bp of the previous stack, then we switch the stack to the irq one | ||
117 | * and save a new bp that links to the previous one. | ||
118 | * (See save_args()) | ||
119 | */ | ||
120 | static inline unsigned long | ||
121 | fixup_bp_irq_link(unsigned long bp, unsigned long *stack, | ||
122 | unsigned long *irq_stack, unsigned long *irq_stack_end) | ||
123 | { | ||
124 | #ifdef CONFIG_FRAME_POINTER | ||
125 | struct stack_frame *frame = (struct stack_frame *)bp; | ||
126 | |||
127 | if (!in_irq_stack(stack, irq_stack, irq_stack_end)) | ||
128 | return (unsigned long)frame->next_frame; | ||
129 | #endif | ||
130 | return bp; | ||
131 | } | ||
132 | |||
104 | /* | 133 | /* |
105 | * x86-64 can have up to three kernel stacks: | 134 | * x86-64 can have up to three kernel stacks: |
106 | * process stack | 135 | * process stack |
@@ -173,7 +202,7 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs, | |||
173 | irq_stack = irq_stack_end - | 202 | irq_stack = irq_stack_end - |
174 | (IRQ_STACK_SIZE - 64) / sizeof(*irq_stack); | 203 | (IRQ_STACK_SIZE - 64) / sizeof(*irq_stack); |
175 | 204 | ||
176 | if (stack >= irq_stack && stack < irq_stack_end) { | 205 | if (in_irq_stack(stack, irq_stack, irq_stack_end)) { |
177 | if (ops->stack(data, "IRQ") < 0) | 206 | if (ops->stack(data, "IRQ") < 0) |
178 | break; | 207 | break; |
179 | bp = print_context_stack(tinfo, stack, bp, | 208 | bp = print_context_stack(tinfo, stack, bp, |
@@ -184,6 +213,8 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs, | |||
184 | * pointer (index -1 to end) in the IRQ stack: | 213 | * pointer (index -1 to end) in the IRQ stack: |
185 | */ | 214 | */ |
186 | stack = (unsigned long *) (irq_stack_end[-1]); | 215 | stack = (unsigned long *) (irq_stack_end[-1]); |
216 | bp = fixup_bp_irq_link(bp, stack, irq_stack, | ||
217 | irq_stack_end); | ||
187 | irq_stack_end = NULL; | 218 | irq_stack_end = NULL; |
188 | ops->stack(data, "EOI"); | 219 | ops->stack(data, "EOI"); |
189 | continue; | 220 | continue; |