aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAtsushi Nemoto <anemo@mba.ocn.ne.jp>2006-02-07 11:48:03 -0500
committerRalf Baechle <ralf@linux-mips.org>2006-02-14 14:13:24 -0500
commit63077519899721120b61d663a68adced068a459d (patch)
tree54ed1587edae13a209f4cd2754ce4487fc3d6376
parent1bdfd0d9632f0254a4fc01d17c9933ae6107dec4 (diff)
[MIPS] Rewrite get_wchan and its helper functions using kallsyms_lookup.
Implement get_wchan() and frame_info_init() using kallsyms_lookup(). This fixes problem with static sched/lock functions and mfinfo[] maintenance issue. If CONFIG_KALLSYMS was disabled, get_wchan() just returns thread_saved_pc() value. Also unwind stackframe based on "addiu sp,-imm" analysis instead of frame pointer. This fixes problem with functions compiled without -fomit-frame-pointer. Signed-off-by: Atsushi Nemoto <anemo@mba.ocn.ne.jp> Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
-rw-r--r--arch/mips/kernel/process.c158
1 files changed, 77 insertions, 81 deletions
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c
index 5232fc752935..092679c2dca9 100644
--- a/arch/mips/kernel/process.c
+++ b/arch/mips/kernel/process.c
@@ -25,6 +25,7 @@
25#include <linux/a.out.h> 25#include <linux/a.out.h>
26#include <linux/init.h> 26#include <linux/init.h>
27#include <linux/completion.h> 27#include <linux/completion.h>
28#include <linux/kallsyms.h>
28 29
29#include <asm/abi.h> 30#include <asm/abi.h>
30#include <asm/bootinfo.h> 31#include <asm/bootinfo.h>
@@ -272,46 +273,19 @@ long kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
272 273
273static struct mips_frame_info { 274static struct mips_frame_info {
274 void *func; 275 void *func;
275 int omit_fp; /* compiled without fno-omit-frame-pointer */ 276 unsigned long func_size;
276 int frame_offset; 277 int frame_size;
277 int pc_offset; 278 int pc_offset;
278} schedule_frame, mfinfo[] = { 279} *schedule_frame, mfinfo[64];
279 { schedule, 0 }, /* must be first */ 280static int mfinfo_num;
280 /* arch/mips/kernel/semaphore.c */
281 { __down, 1 },
282 { __down_interruptible, 1 },
283 /* kernel/sched.c */
284#ifdef CONFIG_PREEMPT
285 { preempt_schedule, 0 },
286#endif
287 { wait_for_completion, 0 },
288 { interruptible_sleep_on, 0 },
289 { interruptible_sleep_on_timeout, 0 },
290 { sleep_on, 0 },
291 { sleep_on_timeout, 0 },
292 { yield, 0 },
293 { io_schedule, 0 },
294 { io_schedule_timeout, 0 },
295#if defined(CONFIG_SMP) && defined(CONFIG_PREEMPT)
296 { __preempt_spin_lock, 0 },
297 { __preempt_write_lock, 0 },
298#endif
299 /* kernel/timer.c */
300 { schedule_timeout, 1 },
301/* { nanosleep_restart, 1 }, */
302 /* lib/rwsem-spinlock.c */
303 { __down_read, 1 },
304 { __down_write, 1 },
305};
306 281
307static int mips_frame_info_initialized;
308static int __init get_frame_info(struct mips_frame_info *info) 282static int __init get_frame_info(struct mips_frame_info *info)
309{ 283{
310 int i; 284 int i;
311 void *func = info->func; 285 void *func = info->func;
312 union mips_instruction *ip = (union mips_instruction *)func; 286 union mips_instruction *ip = (union mips_instruction *)func;
313 info->pc_offset = -1; 287 info->pc_offset = -1;
314 info->frame_offset = info->omit_fp ? 0 : -1; 288 info->frame_size = 0;
315 for (i = 0; i < 128; i++, ip++) { 289 for (i = 0; i < 128; i++, ip++) {
316 /* if jal, jalr, jr, stop. */ 290 /* if jal, jalr, jr, stop. */
317 if (ip->j_format.opcode == jal_op || 291 if (ip->j_format.opcode == jal_op ||
@@ -320,6 +294,23 @@ static int __init get_frame_info(struct mips_frame_info *info)
320 ip->r_format.func == jr_op))) 294 ip->r_format.func == jr_op)))
321 break; 295 break;
322 296
297 if (info->func_size && i >= info->func_size / 4)
298 break;
299 if (
300#ifdef CONFIG_32BIT
301 ip->i_format.opcode == addiu_op &&
302#endif
303#ifdef CONFIG_64BIT
304 ip->i_format.opcode == daddiu_op &&
305#endif
306 ip->i_format.rs == 29 &&
307 ip->i_format.rt == 29) {
308 /* addiu/daddiu sp,sp,-imm */
309 if (info->frame_size)
310 continue;
311 info->frame_size = - ip->i_format.simmediate;
312 }
313
323 if ( 314 if (
324#ifdef CONFIG_32BIT 315#ifdef CONFIG_32BIT
325 ip->i_format.opcode == sw_op && 316 ip->i_format.opcode == sw_op &&
@@ -327,31 +318,20 @@ static int __init get_frame_info(struct mips_frame_info *info)
327#ifdef CONFIG_64BIT 318#ifdef CONFIG_64BIT
328 ip->i_format.opcode == sd_op && 319 ip->i_format.opcode == sd_op &&
329#endif 320#endif
330 ip->i_format.rs == 29) 321 ip->i_format.rs == 29 &&
331 { 322 ip->i_format.rt == 31) {
332 /* sw / sd $ra, offset($sp) */ 323 /* sw / sd $ra, offset($sp) */
333 if (ip->i_format.rt == 31) { 324 if (info->pc_offset != -1)
334 if (info->pc_offset != -1) 325 continue;
335 continue; 326 info->pc_offset =
336 info->pc_offset = 327 ip->i_format.simmediate / sizeof(long);
337 ip->i_format.simmediate / sizeof(long);
338 }
339 /* sw / sd $s8, offset($sp) */
340 if (ip->i_format.rt == 30) {
341//#if 0 /* gcc 3.4 does aggressive optimization... */
342 if (info->frame_offset != -1)
343 continue;
344//#endif
345 info->frame_offset =
346 ip->i_format.simmediate / sizeof(long);
347 }
348 } 328 }
349 } 329 }
350 if (info->pc_offset == -1 || info->frame_offset == -1) { 330 if (info->pc_offset == -1 || info->frame_size == 0) {
351 printk("Can't analyze prologue code at %p\n", func); 331 if (func == schedule)
332 printk("Can't analyze prologue code at %p\n", func);
352 info->pc_offset = -1; 333 info->pc_offset = -1;
353 info->frame_offset = -1; 334 info->frame_size = 0;
354 return -1;
355 } 335 }
356 336
357 return 0; 337 return 0;
@@ -359,25 +339,36 @@ static int __init get_frame_info(struct mips_frame_info *info)
359 339
360static int __init frame_info_init(void) 340static int __init frame_info_init(void)
361{ 341{
362 int i, found; 342 int i;
363 for (i = 0; i < ARRAY_SIZE(mfinfo); i++) 343#ifdef CONFIG_KALLSYMS
364 if (get_frame_info(&mfinfo[i])) 344 char *modname;
365 return -1; 345 char namebuf[KSYM_NAME_LEN + 1];
366 schedule_frame = mfinfo[0]; 346 unsigned long start, size, ofs;
367 /* bubble sort */ 347 extern char __sched_text_start[], __sched_text_end[];
368 do { 348 extern char __lock_text_start[], __lock_text_end[];
369 struct mips_frame_info tmp; 349
370 found = 0; 350 start = (unsigned long)__sched_text_start;
371 for (i = 1; i < ARRAY_SIZE(mfinfo); i++) { 351 for (i = 0; i < ARRAY_SIZE(mfinfo); i++) {
372 if (mfinfo[i-1].func > mfinfo[i].func) { 352 if (start == (unsigned long)schedule)
373 tmp = mfinfo[i]; 353 schedule_frame = &mfinfo[i];
374 mfinfo[i] = mfinfo[i-1]; 354 if (!kallsyms_lookup(start, &size, &ofs, &modname, namebuf))
375 mfinfo[i-1] = tmp; 355 break;
376 found = 1; 356 mfinfo[i].func = (void *)(start + ofs);
377 } 357 mfinfo[i].func_size = size;
378 } 358 start += size - ofs;
379 } while (found); 359 if (start >= (unsigned long)__lock_text_end)
380 mips_frame_info_initialized = 1; 360 break;
361 if (start == (unsigned long)__sched_text_end)
362 start = (unsigned long)__lock_text_start;
363 }
364#else
365 mfinfo[0].func = schedule;
366 schedule_frame = &mfinfo[0];
367#endif
368 for (i = 0; i < ARRAY_SIZE(mfinfo) && mfinfo[i].func; i++)
369 get_frame_info(&mfinfo[i]);
370
371 mfinfo_num = i;
381 return 0; 372 return 0;
382} 373}
383 374
@@ -394,47 +385,52 @@ unsigned long thread_saved_pc(struct task_struct *tsk)
394 if (t->reg31 == (unsigned long) ret_from_fork) 385 if (t->reg31 == (unsigned long) ret_from_fork)
395 return t->reg31; 386 return t->reg31;
396 387
397 if (schedule_frame.pc_offset < 0) 388 if (!schedule_frame || schedule_frame->pc_offset < 0)
398 return 0; 389 return 0;
399 return ((unsigned long *)t->reg29)[schedule_frame.pc_offset]; 390 return ((unsigned long *)t->reg29)[schedule_frame->pc_offset];
400} 391}
401 392
402/* get_wchan - a maintenance nightmare^W^Wpain in the ass ... */ 393/* get_wchan - a maintenance nightmare^W^Wpain in the ass ... */
403unsigned long get_wchan(struct task_struct *p) 394unsigned long get_wchan(struct task_struct *p)
404{ 395{
405 unsigned long stack_page; 396 unsigned long stack_page;
406 unsigned long frame, pc; 397 unsigned long pc;
398#ifdef CONFIG_KALLSYMS
399 unsigned long frame;
400#endif
407 401
408 if (!p || p == current || p->state == TASK_RUNNING) 402 if (!p || p == current || p->state == TASK_RUNNING)
409 return 0; 403 return 0;
410 404
411 stack_page = (unsigned long)task_stack_page(p); 405 stack_page = (unsigned long)task_stack_page(p);
412 if (!stack_page || !mips_frame_info_initialized) 406 if (!stack_page || !mfinfo_num)
413 return 0; 407 return 0;
414 408
415 pc = thread_saved_pc(p); 409 pc = thread_saved_pc(p);
410#ifdef CONFIG_KALLSYMS
416 if (!in_sched_functions(pc)) 411 if (!in_sched_functions(pc))
417 return pc; 412 return pc;
418 413
419 frame = ((unsigned long *)p->thread.reg30)[schedule_frame.frame_offset]; 414 frame = p->thread.reg29 + schedule_frame->frame_size;
420 do { 415 do {
421 int i; 416 int i;
422 417
423 if (frame < stack_page || frame > stack_page + THREAD_SIZE - 32) 418 if (frame < stack_page || frame > stack_page + THREAD_SIZE - 32)
424 return 0; 419 return 0;
425 420
426 for (i = ARRAY_SIZE(mfinfo) - 1; i >= 0; i--) { 421 for (i = mfinfo_num - 1; i >= 0; i--) {
427 if (pc >= (unsigned long) mfinfo[i].func) 422 if (pc >= (unsigned long) mfinfo[i].func)
428 break; 423 break;
429 } 424 }
430 if (i < 0) 425 if (i < 0)
431 break; 426 break;
432 427
433 if (mfinfo[i].omit_fp)
434 break;
435 pc = ((unsigned long *)frame)[mfinfo[i].pc_offset]; 428 pc = ((unsigned long *)frame)[mfinfo[i].pc_offset];
436 frame = ((unsigned long *)frame)[mfinfo[i].frame_offset]; 429 if (!mfinfo[i].frame_size)
430 break;
431 frame += mfinfo[i].frame_size;
437 } while (in_sched_functions(pc)); 432 } while (in_sched_functions(pc));
433#endif
438 434
439 return pc; 435 return pc;
440} 436}