aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/dumpstack_64.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86/kernel/dumpstack_64.c')
-rw-r--r--arch/x86/kernel/dumpstack_64.c118
1 files changed, 84 insertions, 34 deletions
diff --git a/arch/x86/kernel/dumpstack_64.c b/arch/x86/kernel/dumpstack_64.c
index addb207dab92..1abcb50b48ae 100644
--- a/arch/x86/kernel/dumpstack_64.c
+++ b/arch/x86/kernel/dumpstack_64.c
@@ -104,6 +104,44 @@ in_irq_stack(unsigned long *stack, unsigned long *irq_stack,
104 return (stack >= irq_stack && stack < irq_stack_end); 104 return (stack >= irq_stack && stack < irq_stack_end);
105} 105}
106 106
107static const unsigned long irq_stack_size =
108 (IRQ_STACK_SIZE - 64) / sizeof(unsigned long);
109
110enum stack_type {
111 STACK_IS_UNKNOWN,
112 STACK_IS_NORMAL,
113 STACK_IS_EXCEPTION,
114 STACK_IS_IRQ,
115};
116
117static enum stack_type
118analyze_stack(int cpu, struct task_struct *task, unsigned long *stack,
119 unsigned long **stack_end, unsigned long *irq_stack,
120 unsigned *used, char **id)
121{
122 unsigned long addr;
123
124 addr = ((unsigned long)stack & (~(THREAD_SIZE - 1)));
125 if ((unsigned long)task_stack_page(task) == addr)
126 return STACK_IS_NORMAL;
127
128 *stack_end = in_exception_stack(cpu, (unsigned long)stack,
129 used, id);
130 if (*stack_end)
131 return STACK_IS_EXCEPTION;
132
133 if (!irq_stack)
134 return STACK_IS_NORMAL;
135
136 *stack_end = irq_stack;
137 irq_stack = irq_stack - irq_stack_size;
138
139 if (in_irq_stack(stack, irq_stack, *stack_end))
140 return STACK_IS_IRQ;
141
142 return STACK_IS_UNKNOWN;
143}
144
107/* 145/*
108 * x86-64 can have up to three kernel stacks: 146 * x86-64 can have up to three kernel stacks:
109 * process stack 147 * process stack
@@ -116,12 +154,12 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs,
116 const struct stacktrace_ops *ops, void *data) 154 const struct stacktrace_ops *ops, void *data)
117{ 155{
118 const unsigned cpu = get_cpu(); 156 const unsigned cpu = get_cpu();
119 unsigned long *irq_stack_end =
120 (unsigned long *)per_cpu(irq_stack_ptr, cpu);
121 unsigned used = 0;
122 struct thread_info *tinfo; 157 struct thread_info *tinfo;
123 int graph = 0; 158 unsigned long *irq_stack = (unsigned long *)per_cpu(irq_stack_ptr, cpu);
124 unsigned long dummy; 159 unsigned long dummy;
160 unsigned used = 0;
161 int graph = 0;
162 int done = 0;
125 163
126 if (!task) 164 if (!task)
127 task = current; 165 task = current;
@@ -143,49 +181,61 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs,
143 * exceptions 181 * exceptions
144 */ 182 */
145 tinfo = task_thread_info(task); 183 tinfo = task_thread_info(task);
146 for (;;) { 184 while (!done) {
185 unsigned long *stack_end;
186 enum stack_type stype;
147 char *id; 187 char *id;
148 unsigned long *estack_end;
149 estack_end = in_exception_stack(cpu, (unsigned long)stack,
150 &used, &id);
151 188
152 if (estack_end) { 189 stype = analyze_stack(cpu, task, stack, &stack_end,
190 irq_stack, &used, &id);
191
192 /* Default finish unless specified to continue */
193 done = 1;
194
195 switch (stype) {
196
197 /* Break out early if we are on the thread stack */
198 case STACK_IS_NORMAL:
199 break;
200
201 case STACK_IS_EXCEPTION:
202
153 if (ops->stack(data, id) < 0) 203 if (ops->stack(data, id) < 0)
154 break; 204 break;
155 205
156 bp = ops->walk_stack(tinfo, stack, bp, ops, 206 bp = ops->walk_stack(tinfo, stack, bp, ops,
157 data, estack_end, &graph); 207 data, stack_end, &graph);
158 ops->stack(data, "<EOE>"); 208 ops->stack(data, "<EOE>");
159 /* 209 /*
160 * We link to the next stack via the 210 * We link to the next stack via the
161 * second-to-last pointer (index -2 to end) in the 211 * second-to-last pointer (index -2 to end) in the
162 * exception stack: 212 * exception stack:
163 */ 213 */
164 stack = (unsigned long *) estack_end[-2]; 214 stack = (unsigned long *) stack_end[-2];
165 continue; 215 done = 0;
166 } 216 break;
167 if (irq_stack_end) { 217
168 unsigned long *irq_stack; 218 case STACK_IS_IRQ:
169 irq_stack = irq_stack_end - 219
170 (IRQ_STACK_SIZE - 64) / sizeof(*irq_stack); 220 if (ops->stack(data, "IRQ") < 0)
171 221 break;
172 if (in_irq_stack(stack, irq_stack, irq_stack_end)) { 222 bp = ops->walk_stack(tinfo, stack, bp,
173 if (ops->stack(data, "IRQ") < 0) 223 ops, data, stack_end, &graph);
174 break; 224 /*
175 bp = ops->walk_stack(tinfo, stack, bp, 225 * We link to the next stack (which would be
176 ops, data, irq_stack_end, &graph); 226 * the process stack normally) the last
177 /* 227 * pointer (index -1 to end) in the IRQ stack:
178 * We link to the next stack (which would be 228 */
179 * the process stack normally) the last 229 stack = (unsigned long *) (stack_end[-1]);
180 * pointer (index -1 to end) in the IRQ stack: 230 irq_stack = NULL;
181 */ 231 ops->stack(data, "EOI");
182 stack = (unsigned long *) (irq_stack_end[-1]); 232 done = 0;
183 irq_stack_end = NULL; 233 break;
184 ops->stack(data, "EOI"); 234
185 continue; 235 case STACK_IS_UNKNOWN:
186 } 236 ops->stack(data, "UNK");
237 break;
187 } 238 }
188 break;
189 } 239 }
190 240
191 /* 241 /*