|
@@ -23,6 +23,7 @@
|
|
|
#include <linux/mmzone.h>
|
|
|
#include <linux/dcache.h>
|
|
|
#include <linux/fs.h>
|
|
|
+#include <linux/hardirq.h>
|
|
|
#include <linux/string.h>
|
|
|
#include <asm/backtrace.h>
|
|
|
#include <asm/page.h>
|
|
@@ -109,7 +110,7 @@ static struct pt_regs *valid_fault_handler(struct KBacktraceIterator* kbt)
|
|
|
if (kbt->verbose)
|
|
|
pr_err(" <%s while in user mode>\n", fault);
|
|
|
} else {
|
|
|
- if (kbt->verbose)
|
|
|
+ if (kbt->verbose && (p->pc != 0 || p->sp != 0 || p->ex1 != 0))
|
|
|
pr_err(" (odd fault: pc %#lx, sp %#lx, ex1 %#lx?)\n",
|
|
|
p->pc, p->sp, p->ex1);
|
|
|
return NULL;
|
|
@@ -119,10 +120,12 @@ static struct pt_regs *valid_fault_handler(struct KBacktraceIterator* kbt)
|
|
|
return p;
|
|
|
}
|
|
|
|
|
|
-/* Is the pc pointing to a sigreturn trampoline? */
|
|
|
-static int is_sigreturn(unsigned long pc)
|
|
|
+/* Is the iterator pointing to a sigreturn trampoline? */
|
|
|
+static int is_sigreturn(struct KBacktraceIterator *kbt)
|
|
|
{
|
|
|
- return current->mm && (pc == VDSO_SYM(&__vdso_rt_sigreturn));
|
|
|
+ return kbt->task->mm &&
|
|
|
+ (kbt->it.pc == ((ulong)kbt->task->mm->context.vdso_base +
|
|
|
+ (ulong)&__vdso_rt_sigreturn));
|
|
|
}
|
|
|
|
|
|
/* Return a pt_regs pointer for a valid signal handler frame */
|
|
@@ -131,7 +134,7 @@ static struct pt_regs *valid_sigframe(struct KBacktraceIterator* kbt,
|
|
|
{
|
|
|
BacktraceIterator *b = &kbt->it;
|
|
|
|
|
|
- if (is_sigreturn(b->pc) && b->sp < PAGE_OFFSET &&
|
|
|
+ if (is_sigreturn(kbt) && b->sp < PAGE_OFFSET &&
|
|
|
b->sp % sizeof(long) == 0) {
|
|
|
int retval;
|
|
|
pagefault_disable();
|
|
@@ -151,11 +154,6 @@ static struct pt_regs *valid_sigframe(struct KBacktraceIterator* kbt,
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
-static int KBacktraceIterator_is_sigreturn(struct KBacktraceIterator *kbt)
|
|
|
-{
|
|
|
- return is_sigreturn(kbt->it.pc);
|
|
|
-}
|
|
|
-
|
|
|
static int KBacktraceIterator_restart(struct KBacktraceIterator *kbt)
|
|
|
{
|
|
|
struct pt_regs *p;
|
|
@@ -178,7 +176,7 @@ static int KBacktraceIterator_next_item_inclusive(
|
|
|
{
|
|
|
for (;;) {
|
|
|
do {
|
|
|
- if (!KBacktraceIterator_is_sigreturn(kbt))
|
|
|
+ if (!is_sigreturn(kbt))
|
|
|
return KBT_ONGOING;
|
|
|
} while (backtrace_next(&kbt->it));
|
|
|
|
|
@@ -357,51 +355,50 @@ static void describe_addr(struct KBacktraceIterator *kbt,
|
|
|
*/
|
|
|
static bool start_backtrace(void)
|
|
|
{
|
|
|
- if (current->thread.in_backtrace) {
|
|
|
+ if (current_thread_info()->in_backtrace) {
|
|
|
pr_err("Backtrace requested while in backtrace!\n");
|
|
|
return false;
|
|
|
}
|
|
|
- current->thread.in_backtrace = true;
|
|
|
+ current_thread_info()->in_backtrace = true;
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
static void end_backtrace(void)
|
|
|
{
|
|
|
- current->thread.in_backtrace = false;
|
|
|
+ current_thread_info()->in_backtrace = false;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* This method wraps the backtracer's more generic support.
|
|
|
* It is only invoked from the architecture-specific code; show_stack()
|
|
|
- * and dump_stack() (in entry.S) are architecture-independent entry points.
|
|
|
+ * and dump_stack() are architecture-independent entry points.
|
|
|
*/
|
|
|
-void tile_show_stack(struct KBacktraceIterator *kbt, int headers)
|
|
|
+void tile_show_stack(struct KBacktraceIterator *kbt)
|
|
|
{
|
|
|
int i;
|
|
|
int have_mmap_sem = 0;
|
|
|
|
|
|
if (!start_backtrace())
|
|
|
return;
|
|
|
- if (headers) {
|
|
|
- /*
|
|
|
- * Add a blank line since if we are called from panic(),
|
|
|
- * then bust_spinlocks() spit out a space in front of us
|
|
|
- * and it will mess up our KERN_ERR.
|
|
|
- */
|
|
|
- pr_err("Starting stack dump of tid %d, pid %d (%s) on cpu %d at cycle %lld\n",
|
|
|
- kbt->task->pid, kbt->task->tgid, kbt->task->comm,
|
|
|
- raw_smp_processor_id(), get_cycles());
|
|
|
- }
|
|
|
kbt->verbose = 1;
|
|
|
i = 0;
|
|
|
for (; !KBacktraceIterator_end(kbt); KBacktraceIterator_next(kbt)) {
|
|
|
char namebuf[KSYM_NAME_LEN+100];
|
|
|
unsigned long address = kbt->it.pc;
|
|
|
|
|
|
- /* Try to acquire the mmap_sem as we pass into userspace. */
|
|
|
- if (address < PAGE_OFFSET && !have_mmap_sem && kbt->task->mm)
|
|
|
+ /*
|
|
|
+ * Try to acquire the mmap_sem as we pass into userspace.
|
|
|
+ * If we're in an interrupt context, don't even try, since
|
|
|
+ * it's not safe to call e.g. d_path() from an interrupt,
|
|
|
+ * since it uses spin locks without disabling interrupts.
|
|
|
+ * Note we test "kbt->task == current", not "kbt->is_current",
|
|
|
+ * since we're checking that "current" will work in d_path().
|
|
|
+ */
|
|
|
+ if (kbt->task == current && address < PAGE_OFFSET &&
|
|
|
+ !have_mmap_sem && kbt->task->mm && !in_interrupt()) {
|
|
|
have_mmap_sem =
|
|
|
down_read_trylock(&kbt->task->mm->mmap_sem);
|
|
|
+ }
|
|
|
|
|
|
describe_addr(kbt, address, have_mmap_sem,
|
|
|
namebuf, sizeof(namebuf));
|
|
@@ -416,24 +413,12 @@ void tile_show_stack(struct KBacktraceIterator *kbt, int headers)
|
|
|
}
|
|
|
if (kbt->end == KBT_LOOP)
|
|
|
pr_err("Stack dump stopped; next frame identical to this one\n");
|
|
|
- if (headers)
|
|
|
- pr_err("Stack dump complete\n");
|
|
|
if (have_mmap_sem)
|
|
|
up_read(&kbt->task->mm->mmap_sem);
|
|
|
end_backtrace();
|
|
|
}
|
|
|
EXPORT_SYMBOL(tile_show_stack);
|
|
|
|
|
|
-
|
|
|
-/* This is called from show_regs() and _dump_stack() */
|
|
|
-void dump_stack_regs(struct pt_regs *regs)
|
|
|
-{
|
|
|
- struct KBacktraceIterator kbt;
|
|
|
- KBacktraceIterator_init(&kbt, NULL, regs);
|
|
|
- tile_show_stack(&kbt, 1);
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(dump_stack_regs);
|
|
|
-
|
|
|
static struct pt_regs *regs_to_pt_regs(struct pt_regs *regs,
|
|
|
ulong pc, ulong lr, ulong sp, ulong r52)
|
|
|
{
|
|
@@ -445,11 +430,15 @@ static struct pt_regs *regs_to_pt_regs(struct pt_regs *regs,
|
|
|
return regs;
|
|
|
}
|
|
|
|
|
|
-/* This is called from dump_stack() and just converts to pt_regs */
|
|
|
+/* Deprecated function currently only used by kernel_double_fault(). */
|
|
|
void _dump_stack(int dummy, ulong pc, ulong lr, ulong sp, ulong r52)
|
|
|
{
|
|
|
+ struct KBacktraceIterator kbt;
|
|
|
struct pt_regs regs;
|
|
|
- dump_stack_regs(regs_to_pt_regs(®s, pc, lr, sp, r52));
|
|
|
+
|
|
|
+ regs_to_pt_regs(®s, pc, lr, sp, r52);
|
|
|
+ KBacktraceIterator_init(&kbt, NULL, ®s);
|
|
|
+ tile_show_stack(&kbt);
|
|
|
}
|
|
|
|
|
|
/* This is called from KBacktraceIterator_init_current() */
|
|
@@ -461,22 +450,30 @@ void _KBacktraceIterator_init_current(struct KBacktraceIterator *kbt, ulong pc,
|
|
|
regs_to_pt_regs(®s, pc, lr, sp, r52));
|
|
|
}
|
|
|
|
|
|
-/* This is called only from kernel/sched/core.c, with esp == NULL */
|
|
|
+/*
|
|
|
+ * Called from sched_show_task() with task != NULL, or dump_stack()
|
|
|
+ * with task == NULL. The esp argument is always NULL.
|
|
|
+ */
|
|
|
void show_stack(struct task_struct *task, unsigned long *esp)
|
|
|
{
|
|
|
struct KBacktraceIterator kbt;
|
|
|
- if (task == NULL || task == current)
|
|
|
+ if (task == NULL || task == current) {
|
|
|
KBacktraceIterator_init_current(&kbt);
|
|
|
- else
|
|
|
+ KBacktraceIterator_next(&kbt); /* don't show first frame */
|
|
|
+ } else {
|
|
|
KBacktraceIterator_init(&kbt, task, NULL);
|
|
|
- tile_show_stack(&kbt, 0);
|
|
|
+ }
|
|
|
+ tile_show_stack(&kbt);
|
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_STACKTRACE
|
|
|
|
|
|
/* Support generic Linux stack API too */
|
|
|
|
|
|
-void save_stack_trace_tsk(struct task_struct *task, struct stack_trace *trace)
|
|
|
+static void save_stack_trace_common(struct task_struct *task,
|
|
|
+ struct pt_regs *regs,
|
|
|
+ bool user,
|
|
|
+ struct stack_trace *trace)
|
|
|
{
|
|
|
struct KBacktraceIterator kbt;
|
|
|
int skip = trace->skip;
|
|
@@ -484,31 +481,57 @@ void save_stack_trace_tsk(struct task_struct *task, struct stack_trace *trace)
|
|
|
|
|
|
if (!start_backtrace())
|
|
|
goto done;
|
|
|
- if (task == NULL || task == current)
|
|
|
+ if (regs != NULL) {
|
|
|
+ KBacktraceIterator_init(&kbt, NULL, regs);
|
|
|
+ } else if (task == NULL || task == current) {
|
|
|
KBacktraceIterator_init_current(&kbt);
|
|
|
- else
|
|
|
+ skip++; /* don't show KBacktraceIterator_init_current */
|
|
|
+ } else {
|
|
|
KBacktraceIterator_init(&kbt, task, NULL);
|
|
|
+ }
|
|
|
for (; !KBacktraceIterator_end(&kbt); KBacktraceIterator_next(&kbt)) {
|
|
|
if (skip) {
|
|
|
--skip;
|
|
|
continue;
|
|
|
}
|
|
|
- if (i >= trace->max_entries || kbt.it.pc < PAGE_OFFSET)
|
|
|
+ if (i >= trace->max_entries ||
|
|
|
+ (!user && kbt.it.pc < PAGE_OFFSET))
|
|
|
break;
|
|
|
trace->entries[i++] = kbt.it.pc;
|
|
|
}
|
|
|
end_backtrace();
|
|
|
done:
|
|
|
+ if (i < trace->max_entries)
|
|
|
+ trace->entries[i++] = ULONG_MAX;
|
|
|
trace->nr_entries = i;
|
|
|
}
|
|
|
+
|
|
|
+void save_stack_trace_tsk(struct task_struct *task, struct stack_trace *trace)
|
|
|
+{
|
|
|
+ save_stack_trace_common(task, NULL, false, trace);
|
|
|
+}
|
|
|
EXPORT_SYMBOL(save_stack_trace_tsk);
|
|
|
|
|
|
void save_stack_trace(struct stack_trace *trace)
|
|
|
{
|
|
|
- save_stack_trace_tsk(NULL, trace);
|
|
|
+ save_stack_trace_common(NULL, NULL, false, trace);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(save_stack_trace);
|
|
|
|
|
|
+void save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace)
|
|
|
+{
|
|
|
+ save_stack_trace_common(NULL, regs, false, trace);
|
|
|
+}
|
|
|
+
|
|
|
+void save_stack_trace_user(struct stack_trace *trace)
|
|
|
+{
|
|
|
+ /* Trace user stack if we are not a kernel thread. */
|
|
|
+ if (current->mm)
|
|
|
+ save_stack_trace_common(NULL, task_pt_regs(current),
|
|
|
+ true, trace);
|
|
|
+ else if (trace->nr_entries < trace->max_entries)
|
|
|
+ trace->entries[trace->nr_entries++] = ULONG_MAX;
|
|
|
+}
|
|
|
#endif
|
|
|
|
|
|
/* In entry.S */
|