aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/kernel/process.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/kernel/process.c')
-rw-r--r--arch/mips/kernel/process.c132
1 files changed, 50 insertions, 82 deletions
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c
index b160ea30de0..2613a0dd4b8 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, &regs, 0, NULL, NULL); 273 return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);
274} 274}
275 275
276static struct mips_frame_info { 276/*
277 void *func; 277 *
278 unsigned long func_size; 278 */
279 int frame_size; 279struct mips_frame_info {
280 int pc_offset; 280 void *func;
281} *schedule_frame, mfinfo[64]; 281 unsigned long func_size;
282static int mfinfo_num; 282 int frame_size;
283 int pc_offset;
284};
283 285
284static inline int is_ra_save_ins(union mips_instruction *ip) 286static 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
353static struct mips_frame_info schedule_mfi __read_mostly;
354
351static int __init frame_info_init(void) 355static 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 ... */
412unsigned 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 */
451unsigned 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
472out:
473 return pc;
474}