diff options
| author | Dave Kleikamp <shaggy@austin.ibm.com> | 2006-03-14 18:05:45 -0500 |
|---|---|---|
| committer | Dave Kleikamp <shaggy@austin.ibm.com> | 2006-03-14 18:05:45 -0500 |
| commit | c5111f504d2a9b0d258d7c4752b4093523315989 (patch) | |
| tree | 6a52864aff79691689aea21cb0cb928327d5de5b /arch/mips/kernel/process.c | |
| parent | 69eb66d7da7dba2696281981347698e1693c2340 (diff) | |
| parent | a488edc914aa1d766a4e2c982b5ae03d5657ec1b (diff) | |
Merge with /home/shaggy/git/linus-clean/
Diffstat (limited to 'arch/mips/kernel/process.c')
| -rw-r--r-- | arch/mips/kernel/process.c | 163 |
1 files changed, 80 insertions, 83 deletions
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index fa98f10d0132..092679c2dca9 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | * for more details. | 4 | * for more details. |
| 5 | * | 5 | * |
| 6 | * Copyright (C) 1994 - 1999, 2000 by Ralf Baechle and others. | 6 | * Copyright (C) 1994 - 1999, 2000 by Ralf Baechle and others. |
| 7 | * Copyright (C) 2005, 2006 by Ralf Baechle (ralf@linux-mips.org) | ||
| 7 | * Copyright (C) 1999, 2000 Silicon Graphics, Inc. | 8 | * Copyright (C) 1999, 2000 Silicon Graphics, Inc. |
| 8 | * Copyright (C) 2004 Thiemo Seufer | 9 | * Copyright (C) 2004 Thiemo Seufer |
| 9 | */ | 10 | */ |
| @@ -24,6 +25,7 @@ | |||
| 24 | #include <linux/a.out.h> | 25 | #include <linux/a.out.h> |
| 25 | #include <linux/init.h> | 26 | #include <linux/init.h> |
| 26 | #include <linux/completion.h> | 27 | #include <linux/completion.h> |
| 28 | #include <linux/kallsyms.h> | ||
| 27 | 29 | ||
| 28 | #include <asm/abi.h> | 30 | #include <asm/abi.h> |
| 29 | #include <asm/bootinfo.h> | 31 | #include <asm/bootinfo.h> |
| @@ -58,8 +60,8 @@ ATTRIB_NORET void cpu_idle(void) | |||
| 58 | } | 60 | } |
| 59 | } | 61 | } |
| 60 | 62 | ||
| 61 | extern int do_signal(sigset_t *oldset, struct pt_regs *regs); | 63 | extern void do_signal(struct pt_regs *regs); |
| 62 | extern int do_signal32(sigset_t *oldset, struct pt_regs *regs); | 64 | extern void do_signal32(struct pt_regs *regs); |
| 63 | 65 | ||
| 64 | /* | 66 | /* |
| 65 | * Native o32 and N64 ABI without DSP ASE | 67 | * Native o32 and N64 ABI without DSP ASE |
| @@ -271,46 +273,19 @@ long kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) | |||
| 271 | 273 | ||
| 272 | static struct mips_frame_info { | 274 | static struct mips_frame_info { |
| 273 | void *func; | 275 | void *func; |
| 274 | int omit_fp; /* compiled without fno-omit-frame-pointer */ | 276 | unsigned long func_size; |
| 275 | int frame_offset; | 277 | int frame_size; |
| 276 | int pc_offset; | 278 | int pc_offset; |
| 277 | } schedule_frame, mfinfo[] = { | 279 | } *schedule_frame, mfinfo[64]; |
| 278 | { schedule, 0 }, /* must be first */ | 280 | static int mfinfo_num; |
| 279 | /* arch/mips/kernel/semaphore.c */ | ||
| 280 | { __down, 1 }, | ||
| 281 | { __down_interruptible, 1 }, | ||
| 282 | /* kernel/sched.c */ | ||
| 283 | #ifdef CONFIG_PREEMPT | ||
| 284 | { preempt_schedule, 0 }, | ||
| 285 | #endif | ||
| 286 | { wait_for_completion, 0 }, | ||
| 287 | { interruptible_sleep_on, 0 }, | ||
| 288 | { interruptible_sleep_on_timeout, 0 }, | ||
| 289 | { sleep_on, 0 }, | ||
| 290 | { sleep_on_timeout, 0 }, | ||
| 291 | { yield, 0 }, | ||
| 292 | { io_schedule, 0 }, | ||
| 293 | { io_schedule_timeout, 0 }, | ||
| 294 | #if defined(CONFIG_SMP) && defined(CONFIG_PREEMPT) | ||
| 295 | { __preempt_spin_lock, 0 }, | ||
| 296 | { __preempt_write_lock, 0 }, | ||
| 297 | #endif | ||
| 298 | /* kernel/timer.c */ | ||
| 299 | { schedule_timeout, 1 }, | ||
| 300 | /* { nanosleep_restart, 1 }, */ | ||
| 301 | /* lib/rwsem-spinlock.c */ | ||
| 302 | { __down_read, 1 }, | ||
| 303 | { __down_write, 1 }, | ||
| 304 | }; | ||
| 305 | 281 | ||
| 306 | static int mips_frame_info_initialized; | ||
| 307 | static int __init get_frame_info(struct mips_frame_info *info) | 282 | static int __init get_frame_info(struct mips_frame_info *info) |
| 308 | { | 283 | { |
| 309 | int i; | 284 | int i; |
| 310 | void *func = info->func; | 285 | void *func = info->func; |
| 311 | union mips_instruction *ip = (union mips_instruction *)func; | 286 | union mips_instruction *ip = (union mips_instruction *)func; |
| 312 | info->pc_offset = -1; | 287 | info->pc_offset = -1; |
| 313 | info->frame_offset = info->omit_fp ? 0 : -1; | 288 | info->frame_size = 0; |
| 314 | for (i = 0; i < 128; i++, ip++) { | 289 | for (i = 0; i < 128; i++, ip++) { |
| 315 | /* if jal, jalr, jr, stop. */ | 290 | /* if jal, jalr, jr, stop. */ |
| 316 | if (ip->j_format.opcode == jal_op || | 291 | if (ip->j_format.opcode == jal_op || |
| @@ -319,6 +294,23 @@ static int __init get_frame_info(struct mips_frame_info *info) | |||
| 319 | ip->r_format.func == jr_op))) | 294 | ip->r_format.func == jr_op))) |
| 320 | break; | 295 | break; |
| 321 | 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 | |||
| 322 | if ( | 314 | if ( |
| 323 | #ifdef CONFIG_32BIT | 315 | #ifdef CONFIG_32BIT |
| 324 | ip->i_format.opcode == sw_op && | 316 | ip->i_format.opcode == sw_op && |
| @@ -326,31 +318,20 @@ static int __init get_frame_info(struct mips_frame_info *info) | |||
| 326 | #ifdef CONFIG_64BIT | 318 | #ifdef CONFIG_64BIT |
| 327 | ip->i_format.opcode == sd_op && | 319 | ip->i_format.opcode == sd_op && |
| 328 | #endif | 320 | #endif |
| 329 | ip->i_format.rs == 29) | 321 | ip->i_format.rs == 29 && |
| 330 | { | 322 | ip->i_format.rt == 31) { |
| 331 | /* sw / sd $ra, offset($sp) */ | 323 | /* sw / sd $ra, offset($sp) */ |
| 332 | if (ip->i_format.rt == 31) { | 324 | if (info->pc_offset != -1) |
| 333 | if (info->pc_offset != -1) | 325 | continue; |
| 334 | continue; | 326 | info->pc_offset = |
| 335 | info->pc_offset = | 327 | ip->i_format.simmediate / sizeof(long); |
| 336 | ip->i_format.simmediate / sizeof(long); | ||
| 337 | } | ||
| 338 | /* sw / sd $s8, offset($sp) */ | ||
| 339 | if (ip->i_format.rt == 30) { | ||
| 340 | //#if 0 /* gcc 3.4 does aggressive optimization... */ | ||
| 341 | if (info->frame_offset != -1) | ||
| 342 | continue; | ||
| 343 | //#endif | ||
| 344 | info->frame_offset = | ||
| 345 | ip->i_format.simmediate / sizeof(long); | ||
| 346 | } | ||
| 347 | } | 328 | } |
| 348 | } | 329 | } |
| 349 | if (info->pc_offset == -1 || info->frame_offset == -1) { | 330 | if (info->pc_offset == -1 || info->frame_size == 0) { |
| 350 | 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); | ||
| 351 | info->pc_offset = -1; | 333 | info->pc_offset = -1; |
| 352 | info->frame_offset = -1; | 334 | info->frame_size = 0; |
| 353 | return -1; | ||
| 354 | } | 335 | } |
| 355 | 336 | ||
| 356 | return 0; | 337 | return 0; |
| @@ -358,25 +339,36 @@ static int __init get_frame_info(struct mips_frame_info *info) | |||
| 358 | 339 | ||
| 359 | static int __init frame_info_init(void) | 340 | static int __init frame_info_init(void) |
| 360 | { | 341 | { |
| 361 | int i, found; | 342 | int i; |
| 362 | for (i = 0; i < ARRAY_SIZE(mfinfo); i++) | 343 | #ifdef CONFIG_KALLSYMS |
| 363 | if (get_frame_info(&mfinfo[i])) | 344 | char *modname; |
| 364 | return -1; | 345 | char namebuf[KSYM_NAME_LEN + 1]; |
| 365 | schedule_frame = mfinfo[0]; | 346 | unsigned long start, size, ofs; |
| 366 | /* bubble sort */ | 347 | extern char __sched_text_start[], __sched_text_end[]; |
| 367 | do { | 348 | extern char __lock_text_start[], __lock_text_end[]; |
| 368 | struct mips_frame_info tmp; | 349 | |
| 369 | found = 0; | 350 | start = (unsigned long)__sched_text_start; |
| 370 | for (i = 1; i < ARRAY_SIZE(mfinfo); i++) { | 351 | for (i = 0; i < ARRAY_SIZE(mfinfo); i++) { |
| 371 | if (mfinfo[i-1].func > mfinfo[i].func) { | 352 | if (start == (unsigned long)schedule) |
| 372 | tmp = mfinfo[i]; | 353 | schedule_frame = &mfinfo[i]; |
| 373 | mfinfo[i] = mfinfo[i-1]; | 354 | if (!kallsyms_lookup(start, &size, &ofs, &modname, namebuf)) |
| 374 | mfinfo[i-1] = tmp; | 355 | break; |
| 375 | found = 1; | 356 | mfinfo[i].func = (void *)(start + ofs); |
| 376 | } | 357 | mfinfo[i].func_size = size; |
| 377 | } | 358 | start += size - ofs; |
| 378 | } while (found); | 359 | if (start >= (unsigned long)__lock_text_end) |
| 379 | 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; | ||
| 380 | return 0; | 372 | return 0; |
| 381 | } | 373 | } |
| 382 | 374 | ||
| @@ -393,47 +385,52 @@ unsigned long thread_saved_pc(struct task_struct *tsk) | |||
| 393 | if (t->reg31 == (unsigned long) ret_from_fork) | 385 | if (t->reg31 == (unsigned long) ret_from_fork) |
| 394 | return t->reg31; | 386 | return t->reg31; |
| 395 | 387 | ||
| 396 | if (schedule_frame.pc_offset < 0) | 388 | if (!schedule_frame || schedule_frame->pc_offset < 0) |
| 397 | return 0; | 389 | return 0; |
| 398 | return ((unsigned long *)t->reg29)[schedule_frame.pc_offset]; | 390 | return ((unsigned long *)t->reg29)[schedule_frame->pc_offset]; |
| 399 | } | 391 | } |
| 400 | 392 | ||
| 401 | /* get_wchan - a maintenance nightmare^W^Wpain in the ass ... */ | 393 | /* get_wchan - a maintenance nightmare^W^Wpain in the ass ... */ |
| 402 | unsigned long get_wchan(struct task_struct *p) | 394 | unsigned long get_wchan(struct task_struct *p) |
| 403 | { | 395 | { |
| 404 | unsigned long stack_page; | 396 | unsigned long stack_page; |
| 405 | unsigned long frame, pc; | 397 | unsigned long pc; |
| 398 | #ifdef CONFIG_KALLSYMS | ||
| 399 | unsigned long frame; | ||
| 400 | #endif | ||
| 406 | 401 | ||
| 407 | if (!p || p == current || p->state == TASK_RUNNING) | 402 | if (!p || p == current || p->state == TASK_RUNNING) |
| 408 | return 0; | 403 | return 0; |
| 409 | 404 | ||
| 410 | stack_page = (unsigned long)task_stack_page(p); | 405 | stack_page = (unsigned long)task_stack_page(p); |
| 411 | if (!stack_page || !mips_frame_info_initialized) | 406 | if (!stack_page || !mfinfo_num) |
| 412 | return 0; | 407 | return 0; |
| 413 | 408 | ||
| 414 | pc = thread_saved_pc(p); | 409 | pc = thread_saved_pc(p); |
| 410 | #ifdef CONFIG_KALLSYMS | ||
| 415 | if (!in_sched_functions(pc)) | 411 | if (!in_sched_functions(pc)) |
| 416 | return pc; | 412 | return pc; |
| 417 | 413 | ||
| 418 | frame = ((unsigned long *)p->thread.reg30)[schedule_frame.frame_offset]; | 414 | frame = p->thread.reg29 + schedule_frame->frame_size; |
| 419 | do { | 415 | do { |
| 420 | int i; | 416 | int i; |
| 421 | 417 | ||
| 422 | if (frame < stack_page || frame > stack_page + THREAD_SIZE - 32) | 418 | if (frame < stack_page || frame > stack_page + THREAD_SIZE - 32) |
| 423 | return 0; | 419 | return 0; |
| 424 | 420 | ||
| 425 | for (i = ARRAY_SIZE(mfinfo) - 1; i >= 0; i--) { | 421 | for (i = mfinfo_num - 1; i >= 0; i--) { |
| 426 | if (pc >= (unsigned long) mfinfo[i].func) | 422 | if (pc >= (unsigned long) mfinfo[i].func) |
| 427 | break; | 423 | break; |
| 428 | } | 424 | } |
| 429 | if (i < 0) | 425 | if (i < 0) |
| 430 | break; | 426 | break; |
| 431 | 427 | ||
| 432 | if (mfinfo[i].omit_fp) | ||
| 433 | break; | ||
| 434 | pc = ((unsigned long *)frame)[mfinfo[i].pc_offset]; | 428 | pc = ((unsigned long *)frame)[mfinfo[i].pc_offset]; |
| 435 | frame = ((unsigned long *)frame)[mfinfo[i].frame_offset]; | 429 | if (!mfinfo[i].frame_size) |
| 430 | break; | ||
| 431 | frame += mfinfo[i].frame_size; | ||
| 436 | } while (in_sched_functions(pc)); | 432 | } while (in_sched_functions(pc)); |
| 433 | #endif | ||
| 437 | 434 | ||
| 438 | return pc; | 435 | return pc; |
| 439 | } | 436 | } |
