>>>>> On Fri, 02 May 2003 23:04:46 +0900 (JST), Atsushi Nemoto
>>>>> <anemo@mba.ocn.ne.jp> said:
anemo> There are some magic constants about stack layout in
anemo> thread_saved_pc() and get_wchan(). Also I think get_wchan()
anemo> for 32bit kernel is something wrong. (it is not easy to explain
anemo> HOW wrong...)
anemo> This patch (for 2.4 cvs) get rid of these magic constants by
anemo> analyzing kernel codes in runtime.
And this is (untested) 2.5 version of the patch.
diff -ur linux-mips-2.5-cvs/arch/mips/kernel/process.c
linux-2.5.new/arch/mips/kernel/process.c
--- linux-mips-2.5-cvs/arch/mips/kernel/process.c Fri Jan 10 04:16:50 2003
+++ linux-2.5.new/arch/mips/kernel/process.c Sat May 3 21:51:51 2003
@@ -18,6 +18,8 @@
#include <linux/sys.h>
#include <linux/user.h>
#include <linux/a.out.h>
+#include <linux/init.h>
+#include <linux/completion.h>
#include <asm/bootinfo.h>
#include <asm/cpu.h>
@@ -31,6 +33,7 @@
#include <asm/io.h>
#include <asm/elf.h>
#include <asm/isadep.h>
+#include <asm/inst.h>
/*
* We use this if we don't have any better idle routine..
@@ -172,6 +175,65 @@
return retval;
}
+struct mips_frame_info {
+ int frame_offset;
+ int pc_offset;
+};
+static struct mips_frame_info schedule_frame;
+static struct mips_frame_info schedule_timeout_frame;
+static struct mips_frame_info sleep_on_frame;
+static struct mips_frame_info sleep_on_timeout_frame;
+static struct mips_frame_info wait_for_completion_frame;
+static int mips_frame_info_initialized;
+static int __init get_frame_info(struct mips_frame_info *info, void *func)
+{
+ int i;
+ union mips_instruction *ip = (union mips_instruction *)func;
+ info->pc_offset = -1;
+ info->frame_offset = -1;
+ for (i = 0; i < 128; i++, ip++) {
+ /* if jal, jalr, jr, stop. */
+ if (ip->j_format.opcode == jal_op ||
+ (ip->r_format.opcode == spec_op &&
+ (ip->r_format.func == jalr_op ||
+ ip->r_format.func == jr_op)))
+ break;
+ if (ip->i_format.opcode == sw_op &&
+ ip->i_format.rs == 29) {
+ /* sw $ra, offset($sp) */
+ if (ip->i_format.rt == 31) {
+ if (info->pc_offset != -1)
+ break;
+ info->pc_offset =
+ ip->i_format.simmediate / sizeof(long);
+ }
+ /* sw $s8, offset($sp) */
+ if (ip->i_format.rt == 30) {
+ if (info->frame_offset != -1)
+ break;
+ info->frame_offset =
+ ip->i_format.simmediate / sizeof(long);
+ }
+ }
+ }
+ if (info->pc_offset == -1 || info->frame_offset == -1) {
+ printk("Can't analize prologue code at %p\n", func);
+ info->pc_offset = -1;
+ info->frame_offset = -1;
+ return -1;
+ }
+ return 0;
+}
+void __init frame_info_init(void)
+{
+ mips_frame_info_initialized =
+ !get_frame_info(&schedule_frame, schedule) &&
+ !get_frame_info(&schedule_timeout_frame, schedule_timeout) &&
+ !get_frame_info(&sleep_on_frame, sleep_on) &&
+ !get_frame_info(&sleep_on_timeout_frame, sleep_on_timeout) &&
+ !get_frame_info(&wait_for_completion_frame,
wait_for_completion);
+}
+
unsigned long thread_saved_pc(struct thread_struct *t)
{
extern void ret_from_fork(void);
@@ -180,7 +242,9 @@
if (t->reg31 == (unsigned long) ret_from_fork)
return t->reg31;
- return ((unsigned long *)t->reg29)[13];
+ if (schedule_frame.pc_offset < 0)
+ return 0;
+ return ((unsigned long *)t->reg29)[schedule_frame.pc_offset];
}
/*
@@ -199,6 +263,8 @@
if (!p || p == current || p->state == TASK_RUNNING)
return 0;
+ if (!mips_frame_info_initialized)
+ return 0;
pc = thread_saved_pc(&p->thread);
if (pc < first_sched || pc >= last_sched) {
return pc;
@@ -212,29 +278,33 @@
goto schedule_timeout_caller;
if (pc >= (unsigned long)interruptible_sleep_on)
goto schedule_caller;
- /* Fall through */
+ if (pc >= (unsigned long)wait_for_completion)
+ goto schedule_caller;
+ goto schedule_timeout_caller;
schedule_caller:
- pc = ((unsigned long *)p->thread.reg30)[13];
-
+ frame = ((unsigned long *)p->thread.reg30)[schedule_frame.frame_offset];
+ if (pc >= (unsigned long) sleep_on)
+ pc = ((unsigned long *)frame)[sleep_on_frame.pc_offset];
+ else
+ pc = ((unsigned long
*)frame)[wait_for_completion_frame.pc_offset];
return pc;
schedule_timeout_caller:
/*
* The schedule_timeout frame
*/
- frame = ((unsigned long *)p->thread.reg30)[13];
+ frame = ((unsigned long *)p->thread.reg30)[schedule_frame.frame_offset];
/*
* frame now points to sleep_on_timeout's frame
*/
- frame = ((unsigned long *)frame)[9];
- pc = ((unsigned long *)frame)[10];
+ pc = ((unsigned long *)frame)[schedule_timeout_frame.pc_offset];
if (pc >= first_sched && pc < last_sched) {
- /* schedule_timeout called by interruptible_sleep_on_timeout */
- frame = ((unsigned long *)frame)[9];
- pc = ((unsigned long *)frame)[10];
+ /* schedule_timeout called by [interruptible_]sleep_on_timeout
*/
+ frame = ((unsigned long
*)frame)[schedule_timeout_frame.frame_offset];
+ pc = ((unsigned long
*)frame)[sleep_on_timeout_frame.pc_offset];
}
return pc;
diff -ur linux-mips-2.5-cvs/arch/mips/kernel/setup.c
linux-2.5.new/arch/mips/kernel/setup.c
--- linux-mips-2.5-cvs/arch/mips/kernel/setup.c Sat Apr 12 02:28:23 2003
+++ linux-2.5.new/arch/mips/kernel/setup.c Sat May 3 21:25:54 2003
@@ -493,7 +493,9 @@
void au1000_setup(void);
void au1100_setup(void);
void au1500_setup(void);
+ void frame_info_init(void);
+ frame_info_init();
#ifdef CONFIG_BLK_DEV_FD
fd_ops = &no_fd_ops;
#endif
diff -ur linux-mips-2.5-cvs/arch/mips64/kernel/process.c
linux-2.5.new/arch/mips64/kernel/process.c
--- linux-mips-2.5-cvs/arch/mips64/kernel/process.c Fri Feb 21 04:47:25 2003
+++ linux-2.5.new/arch/mips64/kernel/process.c Sat May 3 21:51:45 2003
@@ -19,6 +19,8 @@
#include <linux/sys.h>
#include <linux/user.h>
#include <linux/a.out.h>
+#include <linux/init.h>
+#include <linux/completion.h>
#include <asm/bootinfo.h>
#include <asm/pgtable.h>
@@ -31,6 +33,7 @@
#include <asm/elf.h>
#include <asm/cpu.h>
#include <asm/fpu.h>
+#include <asm/inst.h>
/*
* We use this if we don't have any better idle routine..
@@ -163,6 +166,65 @@
return retval;
}
+struct mips_frame_info {
+ int frame_offset;
+ int pc_offset;
+};
+static struct mips_frame_info schedule_frame;
+static struct mips_frame_info schedule_timeout_frame;
+static struct mips_frame_info sleep_on_frame;
+static struct mips_frame_info sleep_on_timeout_frame;
+static struct mips_frame_info wait_for_completion_frame;
+static int mips_frame_info_initialized;
+static int __init get_frame_info(struct mips_frame_info *info, void *func)
+{
+ int i;
+ union mips_instruction *ip = (union mips_instruction *)func;
+ info->pc_offset = -1;
+ info->frame_offset = -1;
+ for (i = 0; i < 128; i++, ip++) {
+ /* if jal, jalr, jr, stop. */
+ if (ip->j_format.opcode == jal_op ||
+ (ip->r_format.opcode == spec_op &&
+ (ip->r_format.func == jalr_op ||
+ ip->r_format.func == jr_op)))
+ break;
+ if (ip->i_format.opcode == sd_op &&
+ ip->i_format.rs == 29) {
+ /* sd $ra, offset($sp) */
+ if (ip->i_format.rt == 31) {
+ if (info->pc_offset != -1)
+ break;
+ info->pc_offset =
+ ip->i_format.simmediate / sizeof(long);
+ }
+ /* sd $s8, offset($sp) */
+ if (ip->i_format.rt == 30) {
+ if (info->frame_offset != -1)
+ break;
+ info->frame_offset =
+ ip->i_format.simmediate / sizeof(long);
+ }
+ }
+ }
+ if (info->pc_offset == -1 || info->frame_offset == -1) {
+ printk("Can't analize prologue code at %p\n", func);
+ info->pc_offset = -1;
+ info->frame_offset = -1;
+ return -1;
+ }
+ return 0;
+}
+void __init frame_info_init(void)
+{
+ mips_frame_info_initialized =
+ !get_frame_info(&schedule_frame, schedule) &&
+ !get_frame_info(&schedule_timeout_frame, schedule_timeout) &&
+ !get_frame_info(&sleep_on_frame, sleep_on) &&
+ !get_frame_info(&sleep_on_timeout_frame, sleep_on_timeout) &&
+ !get_frame_info(&wait_for_completion_frame,
wait_for_completion);
+}
+
/*
* Return saved PC of a blocked thread.
*/
@@ -174,7 +236,9 @@
if (t->reg31 == (unsigned long) ret_from_fork)
return t->reg31;
- return ((unsigned long*)t->reg29)[11];
+ if (schedule_frame.pc_offset < 0)
+ return 0;
+ return ((unsigned long *)t->reg29)[schedule_frame.pc_offset];
}
/*
@@ -193,6 +257,8 @@
if (!p || p == current || p->state == TASK_RUNNING)
return 0;
+ if (!mips_frame_info_initialized)
+ return 0;
pc = thread_saved_pc(&p->thread);
if (pc < first_sched || pc >= last_sched)
goto out;
@@ -205,26 +271,29 @@
goto schedule_timeout_caller;
if (pc >= (unsigned long)interruptible_sleep_on)
goto schedule_caller;
+ if (pc >= (unsigned long)wait_for_completion)
+ goto schedule_caller;
goto schedule_timeout_caller;
schedule_caller:
- frame = ((unsigned long *)p->thread.reg30)[10];
- pc = ((unsigned long *)frame)[7];
+ frame = ((unsigned long *)p->thread.reg30)[schedule_frame.frame_offset];
+ if (pc >= (unsigned long) sleep_on)
+ pc = ((unsigned long *)frame)[sleep_on_frame.pc_offset];
+ else
+ pc = ((unsigned long
*)frame)[wait_for_completion_frame.pc_offset];
goto out;
schedule_timeout_caller:
/* Must be schedule_timeout ... */
- pc = ((unsigned long *)p->thread.reg30)[11];
- frame = ((unsigned long *)p->thread.reg30)[10];
+ frame = ((unsigned long *)p->thread.reg30)[schedule_frame.frame_offset];
/* The schedule_timeout frame ... */
- pc = ((unsigned long *)frame)[9];
- frame = ((unsigned long *)frame)[8];
+ pc = ((unsigned long *)frame)[schedule_timeout_frame.pc_offset];
if (pc >= first_sched && pc < last_sched) {
- /* schedule_timeout called by interruptible_sleep_on_timeout */
- pc = ((unsigned long *)frame)[7];
- frame = ((unsigned long *)frame)[6];
+ /* schedule_timeout called by [interruptible_]sleep_on_timeout
*/
+ frame = ((unsigned long
*)frame)[schedule_timeout_frame.frame_offset];
+ pc = ((unsigned long
*)frame)[sleep_on_timeout_frame.pc_offset];
}
out:
diff -ur linux-mips-2.5-cvs/arch/mips64/kernel/setup.c
linux-2.5.new/arch/mips64/kernel/setup.c
--- linux-mips-2.5-cvs/arch/mips64/kernel/setup.c Wed Apr 9 09:46:47 2003
+++ linux-2.5.new/arch/mips64/kernel/setup.c Sat May 3 21:25:59 2003
@@ -421,7 +421,9 @@
extern void ip32_setup(void);
extern void swarm_setup(void);
extern void malta_setup(void);
+ void frame_info_init(void);
+ frame_info_init();
#ifdef CONFIG_DECSTATION
decstation_setup();
#endif
---
Atsushi Nemoto
|