diff options
author | Thiemo Seufer <ths@networkno.de> | 2005-02-21 05:55:16 -0500 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2005-10-29 14:30:40 -0400 |
commit | dc953df1ba5526814982676f47580c8e1bcdbfeb (patch) | |
tree | 83adba634d22f0788edbd944586b6f2d2a95bde7 /arch/mips/kernel/process.c | |
parent | 4e6a05fe5f87efd58da16fbf61e1f6329575fcfd (diff) |
Fix wchan implementation, based on earlier by from Atsushi Nemoto.
Signed-off-by: Thiemo Seufer <ths@networkno.de>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/kernel/process.c')
-rw-r--r-- | arch/mips/kernel/process.c | 135 |
1 files changed, 77 insertions, 58 deletions
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index e4f2f8011387..f99efce556ea 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c | |||
@@ -211,22 +211,48 @@ long kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) | |||
211 | return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); | 211 | return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); |
212 | } | 212 | } |
213 | 213 | ||
214 | struct mips_frame_info { | 214 | static struct mips_frame_info { |
215 | void *func; | ||
216 | int omit_fp; /* compiled without fno-omit-frame-pointer */ | ||
215 | int frame_offset; | 217 | int frame_offset; |
216 | int pc_offset; | 218 | int pc_offset; |
219 | } schedule_frame, mfinfo[] = { | ||
220 | { schedule, 0 }, /* must be first */ | ||
221 | /* arch/mips/kernel/semaphore.c */ | ||
222 | { __down, 1 }, | ||
223 | { __down_interruptible, 1 }, | ||
224 | /* kernel/sched.c */ | ||
225 | #ifdef CONFIG_PREEMPT | ||
226 | { preempt_schedule, 0 }, | ||
227 | #endif | ||
228 | { wait_for_completion, 0 }, | ||
229 | { interruptible_sleep_on, 0 }, | ||
230 | { interruptible_sleep_on_timeout, 0 }, | ||
231 | { sleep_on, 0 }, | ||
232 | { sleep_on_timeout, 0 }, | ||
233 | { yield, 0 }, | ||
234 | { io_schedule, 0 }, | ||
235 | { io_schedule_timeout, 0 }, | ||
236 | #if defined(CONFIG_SMP) && defined(CONFIG_PREEMPT) | ||
237 | { __preempt_spin_lock, 0 }, | ||
238 | { __preempt_write_lock, 0 }, | ||
239 | #endif | ||
240 | /* kernel/timer.c */ | ||
241 | { schedule_timeout, 1 }, | ||
242 | /* { nanosleep_restart, 1 }, */ | ||
243 | /* lib/rwsem-spinlock.c */ | ||
244 | { __down_read, 1 }, | ||
245 | { __down_write, 1 }, | ||
217 | }; | 246 | }; |
218 | static struct mips_frame_info schedule_frame; | 247 | |
219 | static struct mips_frame_info schedule_timeout_frame; | ||
220 | static struct mips_frame_info sleep_on_frame; | ||
221 | static struct mips_frame_info sleep_on_timeout_frame; | ||
222 | static struct mips_frame_info wait_for_completion_frame; | ||
223 | static int mips_frame_info_initialized; | 248 | static int mips_frame_info_initialized; |
224 | static int __init get_frame_info(struct mips_frame_info *info, void *func) | 249 | static int __init get_frame_info(struct mips_frame_info *info) |
225 | { | 250 | { |
226 | int i; | 251 | int i; |
252 | void *func = info->func; | ||
227 | union mips_instruction *ip = (union mips_instruction *)func; | 253 | union mips_instruction *ip = (union mips_instruction *)func; |
228 | info->pc_offset = -1; | 254 | info->pc_offset = -1; |
229 | info->frame_offset = -1; | 255 | info->frame_offset = info->omit_fp ? 0 : -1; |
230 | for (i = 0; i < 128; i++, ip++) { | 256 | for (i = 0; i < 128; i++, ip++) { |
231 | /* if jal, jalr, jr, stop. */ | 257 | /* if jal, jalr, jr, stop. */ |
232 | if (ip->j_format.opcode == jal_op || | 258 | if (ip->j_format.opcode == jal_op || |
@@ -247,14 +273,16 @@ static int __init get_frame_info(struct mips_frame_info *info, void *func) | |||
247 | /* sw / sd $ra, offset($sp) */ | 273 | /* sw / sd $ra, offset($sp) */ |
248 | if (ip->i_format.rt == 31) { | 274 | if (ip->i_format.rt == 31) { |
249 | if (info->pc_offset != -1) | 275 | if (info->pc_offset != -1) |
250 | break; | 276 | continue; |
251 | info->pc_offset = | 277 | info->pc_offset = |
252 | ip->i_format.simmediate / sizeof(long); | 278 | ip->i_format.simmediate / sizeof(long); |
253 | } | 279 | } |
254 | /* sw / sd $s8, offset($sp) */ | 280 | /* sw / sd $s8, offset($sp) */ |
255 | if (ip->i_format.rt == 30) { | 281 | if (ip->i_format.rt == 30) { |
282 | //#if 0 /* gcc 3.4 does aggressive optimization... */ | ||
256 | if (info->frame_offset != -1) | 283 | if (info->frame_offset != -1) |
257 | break; | 284 | continue; |
285 | //#endif | ||
258 | info->frame_offset = | 286 | info->frame_offset = |
259 | ip->i_format.simmediate / sizeof(long); | 287 | ip->i_format.simmediate / sizeof(long); |
260 | } | 288 | } |
@@ -272,13 +300,25 @@ static int __init get_frame_info(struct mips_frame_info *info, void *func) | |||
272 | 300 | ||
273 | static int __init frame_info_init(void) | 301 | static int __init frame_info_init(void) |
274 | { | 302 | { |
275 | mips_frame_info_initialized = | 303 | int i, found; |
276 | !get_frame_info(&schedule_frame, schedule) && | 304 | for (i = 0; i < ARRAY_SIZE(mfinfo); i++) |
277 | !get_frame_info(&schedule_timeout_frame, schedule_timeout) && | 305 | if (get_frame_info(&mfinfo[i])) |
278 | !get_frame_info(&sleep_on_frame, sleep_on) && | 306 | return -1; |
279 | !get_frame_info(&sleep_on_timeout_frame, sleep_on_timeout) && | 307 | schedule_frame = mfinfo[0]; |
280 | !get_frame_info(&wait_for_completion_frame, wait_for_completion); | 308 | /* bubble sort */ |
281 | 309 | do { | |
310 | struct mips_frame_info tmp; | ||
311 | found = 0; | ||
312 | for (i = 1; i < ARRAY_SIZE(mfinfo); i++) { | ||
313 | if (mfinfo[i-1].func > mfinfo[i].func) { | ||
314 | tmp = mfinfo[i]; | ||
315 | mfinfo[i] = mfinfo[i-1]; | ||
316 | mfinfo[i-1] = tmp; | ||
317 | found = 1; | ||
318 | } | ||
319 | } | ||
320 | } while (found); | ||
321 | mips_frame_info_initialized = 1; | ||
282 | return 0; | 322 | return 0; |
283 | } | 323 | } |
284 | 324 | ||
@@ -303,60 +343,39 @@ unsigned long thread_saved_pc(struct task_struct *tsk) | |||
303 | /* get_wchan - a maintenance nightmare^W^Wpain in the ass ... */ | 343 | /* get_wchan - a maintenance nightmare^W^Wpain in the ass ... */ |
304 | unsigned long get_wchan(struct task_struct *p) | 344 | unsigned long get_wchan(struct task_struct *p) |
305 | { | 345 | { |
346 | unsigned long stack_page; | ||
306 | unsigned long frame, pc; | 347 | unsigned long frame, pc; |
307 | 348 | ||
308 | if (!p || p == current || p->state == TASK_RUNNING) | 349 | if (!p || p == current || p->state == TASK_RUNNING) |
309 | return 0; | 350 | return 0; |
310 | 351 | ||
311 | if (!mips_frame_info_initialized) | 352 | stack_page = (unsigned long)p->thread_info; |
353 | if (!stack_page || !mips_frame_info_initialized) | ||
312 | return 0; | 354 | return 0; |
355 | |||
313 | pc = thread_saved_pc(p); | 356 | pc = thread_saved_pc(p); |
314 | if (!in_sched_functions(pc)) | 357 | if (!in_sched_functions(pc)) |
315 | goto out; | 358 | return pc; |
316 | |||
317 | if (pc >= (unsigned long) sleep_on_timeout) | ||
318 | goto schedule_timeout_caller; | ||
319 | if (pc >= (unsigned long) sleep_on) | ||
320 | goto schedule_caller; | ||
321 | if (pc >= (unsigned long) interruptible_sleep_on_timeout) | ||
322 | goto schedule_timeout_caller; | ||
323 | if (pc >= (unsigned long)interruptible_sleep_on) | ||
324 | goto schedule_caller; | ||
325 | if (pc >= (unsigned long)wait_for_completion) | ||
326 | goto schedule_caller; | ||
327 | goto schedule_timeout_caller; | ||
328 | |||
329 | schedule_caller: | ||
330 | frame = ((unsigned long *)p->thread.reg30)[schedule_frame.frame_offset]; | ||
331 | if (pc >= (unsigned long) sleep_on) | ||
332 | pc = ((unsigned long *)frame)[sleep_on_frame.pc_offset]; | ||
333 | else | ||
334 | pc = ((unsigned long *)frame)[wait_for_completion_frame.pc_offset]; | ||
335 | goto out; | ||
336 | 359 | ||
337 | schedule_timeout_caller: | ||
338 | /* | ||
339 | * The schedule_timeout frame | ||
340 | */ | ||
341 | frame = ((unsigned long *)p->thread.reg30)[schedule_frame.frame_offset]; | 360 | frame = ((unsigned long *)p->thread.reg30)[schedule_frame.frame_offset]; |
361 | do { | ||
362 | int i; | ||
342 | 363 | ||
343 | /* | 364 | if (frame < stack_page || frame > stack_page + THREAD_SIZE - 32) |
344 | * frame now points to sleep_on_timeout's frame | 365 | return 0; |
345 | */ | ||
346 | pc = ((unsigned long *)frame)[schedule_timeout_frame.pc_offset]; | ||
347 | 366 | ||
348 | if (in_sched_functions(pc)) { | 367 | for (i = ARRAY_SIZE(mfinfo) - 1; i >= 0; i--) { |
349 | /* schedule_timeout called by [interruptible_]sleep_on_timeout */ | 368 | if (pc >= (unsigned long) mfinfo[i].func) |
350 | frame = ((unsigned long *)frame)[schedule_timeout_frame.frame_offset]; | 369 | break; |
351 | pc = ((unsigned long *)frame)[sleep_on_timeout_frame.pc_offset]; | 370 | } |
352 | } | 371 | if (i < 0) |
353 | 372 | break; | |
354 | out: | ||
355 | 373 | ||
356 | #ifdef CONFIG_64BIT | 374 | if (mfinfo[i].omit_fp) |
357 | if (current->thread.mflags & MF_32BIT_REGS) /* Kludge for 32-bit ps */ | 375 | break; |
358 | pc &= 0xffffffffUL; | 376 | pc = ((unsigned long *)frame)[mfinfo[i].pc_offset]; |
359 | #endif | 377 | frame = ((unsigned long *)frame)[mfinfo[i].frame_offset]; |
378 | } while (in_sched_functions(pc)); | ||
360 | 379 | ||
361 | return pc; | 380 | return pc; |
362 | } | 381 | } |