diff options
Diffstat (limited to 'arch/mips')
-rw-r--r-- | arch/mips/kernel/process.c | 132 |
1 files changed, 50 insertions, 82 deletions
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index b160ea30de0f..2613a0dd4b82 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c | |||
@@ -273,13 +273,15 @@ long kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) | |||
273 | return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); | 273 | return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); |
274 | } | 274 | } |
275 | 275 | ||
276 | static struct mips_frame_info { | 276 | /* |
277 | void *func; | 277 | * |
278 | unsigned long func_size; | 278 | */ |
279 | int frame_size; | 279 | struct mips_frame_info { |
280 | int pc_offset; | 280 | void *func; |
281 | } *schedule_frame, mfinfo[64]; | 281 | unsigned long func_size; |
282 | static int mfinfo_num; | 282 | int frame_size; |
283 | int pc_offset; | ||
284 | }; | ||
283 | 285 | ||
284 | static inline int is_ra_save_ins(union mips_instruction *ip) | 286 | static inline int is_ra_save_ins(union mips_instruction *ip) |
285 | { | 287 | { |
@@ -348,45 +350,30 @@ err: | |||
348 | return -1; | 350 | return -1; |
349 | } | 351 | } |
350 | 352 | ||
353 | static struct mips_frame_info schedule_mfi __read_mostly; | ||
354 | |||
351 | static int __init frame_info_init(void) | 355 | static int __init frame_info_init(void) |
352 | { | 356 | { |
353 | int i; | 357 | unsigned long size = 0; |
354 | #ifdef CONFIG_KALLSYMS | 358 | #ifdef CONFIG_KALLSYMS |
359 | unsigned long ofs; | ||
355 | char *modname; | 360 | char *modname; |
356 | char namebuf[KSYM_NAME_LEN + 1]; | 361 | char namebuf[KSYM_NAME_LEN + 1]; |
357 | unsigned long start, size, ofs; | 362 | |
358 | extern char __sched_text_start[], __sched_text_end[]; | 363 | kallsyms_lookup((unsigned long)schedule, &size, &ofs, &modname, namebuf); |
359 | extern char __lock_text_start[], __lock_text_end[]; | ||
360 | |||
361 | start = (unsigned long)__sched_text_start; | ||
362 | for (i = 0; i < ARRAY_SIZE(mfinfo); i++) { | ||
363 | if (start == (unsigned long)schedule) | ||
364 | schedule_frame = &mfinfo[i]; | ||
365 | if (!kallsyms_lookup(start, &size, &ofs, &modname, namebuf)) | ||
366 | break; | ||
367 | mfinfo[i].func = (void *)(start + ofs); | ||
368 | mfinfo[i].func_size = size; | ||
369 | start += size - ofs; | ||
370 | if (start >= (unsigned long)__lock_text_end) | ||
371 | break; | ||
372 | if (start == (unsigned long)__sched_text_end) | ||
373 | start = (unsigned long)__lock_text_start; | ||
374 | } | ||
375 | #else | ||
376 | mfinfo[0].func = schedule; | ||
377 | schedule_frame = &mfinfo[0]; | ||
378 | #endif | 364 | #endif |
379 | for (i = 0; i < ARRAY_SIZE(mfinfo) && mfinfo[i].func; i++) | 365 | schedule_mfi.func = schedule; |
380 | get_frame_info(mfinfo + i); | 366 | schedule_mfi.func_size = size; |
367 | |||
368 | get_frame_info(&schedule_mfi); | ||
381 | 369 | ||
382 | /* | 370 | /* |
383 | * Without schedule() frame info, result given by | 371 | * Without schedule() frame info, result given by |
384 | * thread_saved_pc() and get_wchan() are not reliable. | 372 | * thread_saved_pc() and get_wchan() are not reliable. |
385 | */ | 373 | */ |
386 | if (schedule_frame->pc_offset < 0) | 374 | if (schedule_mfi.pc_offset < 0) |
387 | printk("Can't analyze schedule() prologue at %p\n", schedule); | 375 | printk("Can't analyze schedule() prologue at %p\n", schedule); |
388 | 376 | ||
389 | mfinfo_num = i; | ||
390 | return 0; | 377 | return 0; |
391 | } | 378 | } |
392 | 379 | ||
@@ -402,58 +389,11 @@ unsigned long thread_saved_pc(struct task_struct *tsk) | |||
402 | /* New born processes are a special case */ | 389 | /* New born processes are a special case */ |
403 | if (t->reg31 == (unsigned long) ret_from_fork) | 390 | if (t->reg31 == (unsigned long) ret_from_fork) |
404 | return t->reg31; | 391 | return t->reg31; |
405 | 392 | if (schedule_mfi.pc_offset < 0) | |
406 | if (!schedule_frame || schedule_frame->pc_offset < 0) | ||
407 | return 0; | 393 | return 0; |
408 | return ((unsigned long *)t->reg29)[schedule_frame->pc_offset]; | 394 | return ((unsigned long *)t->reg29)[schedule_mfi.pc_offset]; |
409 | } | 395 | } |
410 | 396 | ||
411 | /* get_wchan - a maintenance nightmare^W^Wpain in the ass ... */ | ||
412 | unsigned long get_wchan(struct task_struct *p) | ||
413 | { | ||
414 | unsigned long stack_page; | ||
415 | unsigned long pc; | ||
416 | #ifdef CONFIG_KALLSYMS | ||
417 | unsigned long frame; | ||
418 | #endif | ||
419 | |||
420 | if (!p || p == current || p->state == TASK_RUNNING) | ||
421 | return 0; | ||
422 | |||
423 | stack_page = (unsigned long)task_stack_page(p); | ||
424 | if (!stack_page || !mfinfo_num) | ||
425 | return 0; | ||
426 | |||
427 | pc = thread_saved_pc(p); | ||
428 | #ifdef CONFIG_KALLSYMS | ||
429 | if (!in_sched_functions(pc)) | ||
430 | return pc; | ||
431 | |||
432 | frame = p->thread.reg29 + schedule_frame->frame_size; | ||
433 | do { | ||
434 | int i; | ||
435 | |||
436 | if (frame < stack_page || frame > stack_page + THREAD_SIZE - 32) | ||
437 | return 0; | ||
438 | |||
439 | for (i = mfinfo_num - 1; i >= 0; i--) { | ||
440 | if (pc >= (unsigned long) mfinfo[i].func) | ||
441 | break; | ||
442 | } | ||
443 | if (i < 0) | ||
444 | break; | ||
445 | |||
446 | if (mfinfo[i].pc_offset < 0) | ||
447 | break; | ||
448 | pc = ((unsigned long *)frame)[mfinfo[i].pc_offset]; | ||
449 | if (!mfinfo[i].frame_size) | ||
450 | break; | ||
451 | frame += mfinfo[i].frame_size; | ||
452 | } while (in_sched_functions(pc)); | ||
453 | #endif | ||
454 | |||
455 | return pc; | ||
456 | } | ||
457 | 397 | ||
458 | #ifdef CONFIG_KALLSYMS | 398 | #ifdef CONFIG_KALLSYMS |
459 | /* used by show_backtrace() */ | 399 | /* used by show_backtrace() */ |
@@ -504,3 +444,31 @@ unsigned long unwind_stack(struct task_struct *task, unsigned long *sp, | |||
504 | return __kernel_text_address(pc) ? pc : 0; | 444 | return __kernel_text_address(pc) ? pc : 0; |
505 | } | 445 | } |
506 | #endif | 446 | #endif |
447 | |||
448 | /* | ||
449 | * get_wchan - a maintenance nightmare^W^Wpain in the ass ... | ||
450 | */ | ||
451 | unsigned long get_wchan(struct task_struct *task) | ||
452 | { | ||
453 | unsigned long pc = 0; | ||
454 | #ifdef CONFIG_KALLSYMS | ||
455 | unsigned long sp; | ||
456 | #endif | ||
457 | |||
458 | if (!task || task == current || task->state == TASK_RUNNING) | ||
459 | goto out; | ||
460 | if (!task_stack_page(task)) | ||
461 | goto out; | ||
462 | |||
463 | pc = thread_saved_pc(task); | ||
464 | |||
465 | #ifdef CONFIG_KALLSYMS | ||
466 | sp = task->thread.reg29 + schedule_mfi.frame_size; | ||
467 | |||
468 | while (in_sched_functions(pc)) | ||
469 | pc = unwind_stack(task, &sp, pc, 0); | ||
470 | #endif | ||
471 | |||
472 | out: | ||
473 | return pc; | ||
474 | } | ||