Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 63077519 authored by Atsushi Nemoto's avatar Atsushi Nemoto Committed by Ralf Baechle
Browse files

[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: default avatarAtsushi Nemoto <anemo@mba.ocn.ne.jp>
Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent 1bdfd0d9
Loading
Loading
Loading
Loading
+77 −81
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@
#include <linux/a.out.h>
#include <linux/init.h>
#include <linux/completion.h>
#include <linux/kallsyms.h>

#include <asm/abi.h>
#include <asm/bootinfo.h>
@@ -272,46 +273,19 @@ long kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)

static struct mips_frame_info {
	void *func;
	int omit_fp;	/* compiled without fno-omit-frame-pointer */
	int frame_offset;
	unsigned long func_size;
	int frame_size;
	int pc_offset;
} schedule_frame, mfinfo[] = {
	{ schedule, 0 },	/* must be first */
	/* arch/mips/kernel/semaphore.c */
	{ __down, 1 },
	{ __down_interruptible, 1 },
	/* kernel/sched.c */
#ifdef CONFIG_PREEMPT
	{ preempt_schedule, 0 },
#endif
	{ wait_for_completion, 0 },
	{ interruptible_sleep_on, 0 },
	{ interruptible_sleep_on_timeout, 0 },
	{ sleep_on, 0 },
	{ sleep_on_timeout, 0 },
	{ yield, 0 },
	{ io_schedule, 0 },
	{ io_schedule_timeout, 0 },
#if defined(CONFIG_SMP) && defined(CONFIG_PREEMPT)
	{ __preempt_spin_lock, 0 },
	{ __preempt_write_lock, 0 },
#endif
	/* kernel/timer.c */
	{ schedule_timeout, 1 },
/*	{ nanosleep_restart, 1 }, */
	/* lib/rwsem-spinlock.c */
	{ __down_read, 1 },
	{ __down_write, 1 },
};
} *schedule_frame, mfinfo[64];
static int mfinfo_num;

static int mips_frame_info_initialized;
static int __init get_frame_info(struct mips_frame_info *info)
{
	int i;
	void *func = info->func;
	union mips_instruction *ip = (union mips_instruction *)func;
	info->pc_offset = -1;
	info->frame_offset = info->omit_fp ? 0 : -1;
	info->frame_size = 0;
	for (i = 0; i < 128; i++, ip++) {
		/* if jal, jalr, jr, stop. */
		if (ip->j_format.opcode == jal_op ||
@@ -320,6 +294,23 @@ static int __init get_frame_info(struct mips_frame_info *info)
		      ip->r_format.func == jr_op)))
			break;

		if (info->func_size && i >= info->func_size / 4)
			break;
		if (
#ifdef CONFIG_32BIT
		    ip->i_format.opcode == addiu_op &&
#endif
#ifdef CONFIG_64BIT
		    ip->i_format.opcode == daddiu_op &&
#endif
		    ip->i_format.rs == 29 &&
		    ip->i_format.rt == 29) {
			/* addiu/daddiu sp,sp,-imm */
			if (info->frame_size)
				continue;
			info->frame_size = - ip->i_format.simmediate;
		}

		if (
#ifdef CONFIG_32BIT
		    ip->i_format.opcode == sw_op &&
@@ -327,31 +318,20 @@ static int __init get_frame_info(struct mips_frame_info *info)
#ifdef CONFIG_64BIT
		    ip->i_format.opcode == sd_op &&
#endif
		    ip->i_format.rs == 29)
		{
		    ip->i_format.rs == 29 &&
		    ip->i_format.rt == 31) {
			/* sw / sd $ra, offset($sp) */
			if (ip->i_format.rt == 31) {
			if (info->pc_offset != -1)
				continue;
			info->pc_offset =
				ip->i_format.simmediate / sizeof(long);
		}
			/* sw / sd $s8, offset($sp) */
			if (ip->i_format.rt == 30) {
//#if 0	/* gcc 3.4 does aggressive optimization... */
				if (info->frame_offset != -1)
					continue;
//#endif
				info->frame_offset =
					ip->i_format.simmediate / sizeof(long);
	}
		}
	}
	if (info->pc_offset == -1 || info->frame_offset == -1) {
	if (info->pc_offset == -1 || info->frame_size == 0) {
		if (func == schedule)
			printk("Can't analyze prologue code at %p\n", func);
		info->pc_offset = -1;
		info->frame_offset = -1;
		return -1;
		info->frame_size = 0;
	}

	return 0;
@@ -359,25 +339,36 @@ static int __init get_frame_info(struct mips_frame_info *info)

static int __init frame_info_init(void)
{
	int i, found;
	for (i = 0; i < ARRAY_SIZE(mfinfo); i++)
		if (get_frame_info(&mfinfo[i]))
			return -1;
	schedule_frame = mfinfo[0];
	/* bubble sort */
	do {
		struct mips_frame_info tmp;
		found = 0;
		for (i = 1; i < ARRAY_SIZE(mfinfo); i++) {
			if (mfinfo[i-1].func > mfinfo[i].func) {
				tmp = mfinfo[i];
				mfinfo[i] = mfinfo[i-1];
				mfinfo[i-1] = tmp;
				found = 1;
			}
	int i;
#ifdef CONFIG_KALLSYMS
	char *modname;
	char namebuf[KSYM_NAME_LEN + 1];
	unsigned long start, size, ofs;
	extern char __sched_text_start[], __sched_text_end[];
	extern char __lock_text_start[], __lock_text_end[];

	start = (unsigned long)__sched_text_start;
	for (i = 0; i < ARRAY_SIZE(mfinfo); i++) {
		if (start == (unsigned long)schedule)
			schedule_frame = &mfinfo[i];
		if (!kallsyms_lookup(start, &size, &ofs, &modname, namebuf))
			break;
		mfinfo[i].func = (void *)(start + ofs);
		mfinfo[i].func_size = size;
		start += size - ofs;
		if (start >= (unsigned long)__lock_text_end)
			break;
		if (start == (unsigned long)__sched_text_end)
			start = (unsigned long)__lock_text_start;
	}
	} while (found);
	mips_frame_info_initialized = 1;
#else
	mfinfo[0].func = schedule;
	schedule_frame = &mfinfo[0];
#endif
	for (i = 0; i < ARRAY_SIZE(mfinfo) && mfinfo[i].func; i++)
		get_frame_info(&mfinfo[i]);

	mfinfo_num = i;
	return 0;
}

@@ -394,47 +385,52 @@ unsigned long thread_saved_pc(struct task_struct *tsk)
	if (t->reg31 == (unsigned long) ret_from_fork)
		return t->reg31;

	if (schedule_frame.pc_offset < 0)
	if (!schedule_frame || schedule_frame->pc_offset < 0)
		return 0;
	return ((unsigned long *)t->reg29)[schedule_frame.pc_offset];
	return ((unsigned long *)t->reg29)[schedule_frame->pc_offset];
}

/* get_wchan - a maintenance nightmare^W^Wpain in the ass ...  */
unsigned long get_wchan(struct task_struct *p)
{
	unsigned long stack_page;
	unsigned long frame, pc;
	unsigned long pc;
#ifdef CONFIG_KALLSYMS
	unsigned long frame;
#endif

	if (!p || p == current || p->state == TASK_RUNNING)
		return 0;

	stack_page = (unsigned long)task_stack_page(p);
	if (!stack_page || !mips_frame_info_initialized)
	if (!stack_page || !mfinfo_num)
		return 0;

	pc = thread_saved_pc(p);
#ifdef CONFIG_KALLSYMS
	if (!in_sched_functions(pc))
		return pc;

	frame = ((unsigned long *)p->thread.reg30)[schedule_frame.frame_offset];
	frame = p->thread.reg29 + schedule_frame->frame_size;
	do {
		int i;

		if (frame < stack_page || frame > stack_page + THREAD_SIZE - 32)
			return 0;

		for (i = ARRAY_SIZE(mfinfo) - 1; i >= 0; i--) {
		for (i = mfinfo_num - 1; i >= 0; i--) {
			if (pc >= (unsigned long) mfinfo[i].func)
				break;
		}
		if (i < 0)
			break;

		if (mfinfo[i].omit_fp)
			break;
		pc = ((unsigned long *)frame)[mfinfo[i].pc_offset];
		frame = ((unsigned long *)frame)[mfinfo[i].frame_offset];
		if (!mfinfo[i].frame_size)
			break;
		frame += mfinfo[i].frame_size;
	} while (in_sched_functions(pc));
#endif

	return pc;
}