diff options
Diffstat (limited to 'arch/sh/kernel/unwinder.c')
-rw-r--r-- | arch/sh/kernel/unwinder.c | 33 |
1 files changed, 27 insertions, 6 deletions
diff --git a/arch/sh/kernel/unwinder.c b/arch/sh/kernel/unwinder.c index 2b30fa28b440..b9c122abe251 100644 --- a/arch/sh/kernel/unwinder.c +++ b/arch/sh/kernel/unwinder.c | |||
@@ -53,8 +53,6 @@ static struct list_head unwinder_list = { | |||
53 | 53 | ||
54 | static DEFINE_SPINLOCK(unwinder_lock); | 54 | static DEFINE_SPINLOCK(unwinder_lock); |
55 | 55 | ||
56 | static atomic_t unwinder_running = ATOMIC_INIT(0); | ||
57 | |||
58 | /** | 56 | /** |
59 | * select_unwinder - Select the best registered stack unwinder. | 57 | * select_unwinder - Select the best registered stack unwinder. |
60 | * | 58 | * |
@@ -122,6 +120,8 @@ int unwinder_register(struct unwinder *u) | |||
122 | return ret; | 120 | return ret; |
123 | } | 121 | } |
124 | 122 | ||
123 | int unwinder_faulted = 0; | ||
124 | |||
125 | /* | 125 | /* |
126 | * Unwind the call stack and pass information to the stacktrace_ops | 126 | * Unwind the call stack and pass information to the stacktrace_ops |
127 | * functions. Also handle the case where we need to switch to a new | 127 | * functions. Also handle the case where we need to switch to a new |
@@ -144,19 +144,40 @@ void unwind_stack(struct task_struct *task, struct pt_regs *regs, | |||
144 | * Hopefully this will give us a semi-reliable stacktrace so we | 144 | * Hopefully this will give us a semi-reliable stacktrace so we |
145 | * can diagnose why curr_unwinder->dump() faulted. | 145 | * can diagnose why curr_unwinder->dump() faulted. |
146 | */ | 146 | */ |
147 | if (atomic_inc_return(&unwinder_running) != 1) { | 147 | if (unwinder_faulted) { |
148 | spin_lock_irqsave(&unwinder_lock, flags); | 148 | spin_lock_irqsave(&unwinder_lock, flags); |
149 | 149 | ||
150 | if (!list_is_singular(&unwinder_list)) { | 150 | /* Make sure no one beat us to changing the unwinder */ |
151 | if (unwinder_faulted && !list_is_singular(&unwinder_list)) { | ||
151 | list_del(&curr_unwinder->list); | 152 | list_del(&curr_unwinder->list); |
152 | curr_unwinder = select_unwinder(); | 153 | curr_unwinder = select_unwinder(); |
154 | |||
155 | unwinder_faulted = 0; | ||
153 | } | 156 | } |
154 | 157 | ||
155 | spin_unlock_irqrestore(&unwinder_lock, flags); | 158 | spin_unlock_irqrestore(&unwinder_lock, flags); |
156 | atomic_dec(&unwinder_running); | ||
157 | } | 159 | } |
158 | 160 | ||
159 | curr_unwinder->dump(task, regs, sp, ops, data); | 161 | curr_unwinder->dump(task, regs, sp, ops, data); |
162 | } | ||
163 | |||
164 | /* | ||
165 | * Trap handler for UWINDER_BUG() statements. We must switch to the | ||
166 | * unwinder with the next highest rating. | ||
167 | */ | ||
168 | BUILD_TRAP_HANDLER(unwinder) | ||
169 | { | ||
170 | insn_size_t insn; | ||
171 | TRAP_HANDLER_DECL; | ||
172 | |||
173 | /* Rewind */ | ||
174 | regs->pc -= instruction_size(ctrl_inw(regs->pc - 4)); | ||
175 | insn = *(insn_size_t *)instruction_pointer(regs); | ||
176 | |||
177 | /* Switch unwinders when unwind_stack() is called */ | ||
178 | unwinder_faulted = 1; | ||
160 | 179 | ||
161 | atomic_dec(&unwinder_running); | 180 | #ifdef CONFIG_BUG |
181 | handle_BUG(regs); | ||
182 | #endif | ||
162 | } | 183 | } |