Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | /* * Copyright (C) 2009 Matt Fleming * * Based, in part, on kernel/time/clocksource.c. * * This file provides arbitration code for stack unwinders. * * Multiple stack unwinders can be available on a system, usually with * the most accurate unwinder being the currently active one. */ #include <linux/errno.h> #include <linux/list.h> #include <linux/spinlock.h> #include <linux/module.h> #include <asm/unwinder.h> #include <linux/atomic.h> /* * This is the most basic stack unwinder an architecture can * provide. For architectures without reliable frame pointers, e.g. * RISC CPUs, it can be implemented by looking through the stack for * addresses that lie within the kernel text section. * * Other CPUs, e.g. x86, can use their frame pointer register to * construct more accurate stack traces. */ static struct list_head unwinder_list; static struct unwinder stack_reader = { .name = "stack-reader", .dump = stack_reader_dump, .rating = 50, .list = { .next = &unwinder_list, .prev = &unwinder_list, }, }; /* * "curr_unwinder" points to the stack unwinder currently in use. This * is the unwinder with the highest rating. * * "unwinder_list" is a linked-list of all available unwinders, sorted * by rating. * * All modifications of "curr_unwinder" and "unwinder_list" must be * performed whilst holding "unwinder_lock". */ static struct unwinder *curr_unwinder = &stack_reader; static struct list_head unwinder_list = { .next = &stack_reader.list, .prev = &stack_reader.list, }; static DEFINE_SPINLOCK(unwinder_lock); /** * select_unwinder - Select the best registered stack unwinder. * * Private function. Must hold unwinder_lock when called. * * Select the stack unwinder with the best rating. This is useful for * setting up curr_unwinder. */ static struct unwinder *select_unwinder(void) { struct unwinder *best; if (list_empty(&unwinder_list)) return NULL; best = list_entry(unwinder_list.next, struct unwinder, list); if (best == curr_unwinder) return NULL; return best; } /* * Enqueue the stack unwinder sorted by rating. */ static int unwinder_enqueue(struct unwinder *ops) { struct list_head *tmp, *entry = &unwinder_list; list_for_each(tmp, &unwinder_list) { struct unwinder *o; o = list_entry(tmp, struct unwinder, list); if (o == ops) return -EBUSY; /* Keep track of the place, where to insert */ if (o->rating >= ops->rating) entry = tmp; } list_add(&ops->list, entry); return 0; } /** * unwinder_register - Used to install new stack unwinder * @u: unwinder to be registered * * Install the new stack unwinder on the unwinder list, which is sorted * by rating. * * Returns -EBUSY if registration fails, zero otherwise. */ int unwinder_register(struct unwinder *u) { unsigned long flags; int ret; spin_lock_irqsave(&unwinder_lock, flags); ret = unwinder_enqueue(u); if (!ret) curr_unwinder = select_unwinder(); spin_unlock_irqrestore(&unwinder_lock, flags); return ret; } int unwinder_faulted = 0; /* * Unwind the call stack and pass information to the stacktrace_ops * functions. Also handle the case where we need to switch to a new * stack dumper because the current one faulted unexpectedly. */ void unwind_stack(struct task_struct *task, struct pt_regs *regs, unsigned long *sp, const struct stacktrace_ops *ops, void *data) { unsigned long flags; /* * The problem with unwinders with high ratings is that they are * inherently more complicated than the simple ones with lower * ratings. We are therefore more likely to fault in the * complicated ones, e.g. hitting BUG()s. If we fault in the * code for the current stack unwinder we try to downgrade to * one with a lower rating. * * Hopefully this will give us a semi-reliable stacktrace so we * can diagnose why curr_unwinder->dump() faulted. */ if (unwinder_faulted) { spin_lock_irqsave(&unwinder_lock, flags); /* Make sure no one beat us to changing the unwinder */ if (unwinder_faulted && !list_is_singular(&unwinder_list)) { list_del(&curr_unwinder->list); curr_unwinder = select_unwinder(); unwinder_faulted = 0; } spin_unlock_irqrestore(&unwinder_lock, flags); } curr_unwinder->dump(task, regs, sp, ops, data); } EXPORT_SYMBOL_GPL(unwind_stack); |