Commit 018cce33 authored by Christophe Leroy's avatar Christophe Leroy Committed by Michael Ellerman
Browse files

powerpc: prep stack walkers for THREAD_INFO_IN_TASK



[text copied from commit 9bbd4c56
("arm64: prep stack walkers for THREAD_INFO_IN_TASK")]

When CONFIG_THREAD_INFO_IN_TASK is selected, task stacks may be freed
before a task is destroyed. To account for this, the stacks are
refcounted, and when manipulating the stack of another task, it is
necessary to get/put the stack to ensure it isn't freed and/or re-used
while we do so.

This patch reworks the powerpc stack walking code to account for this.
When CONFIG_THREAD_INFO_IN_TASK is not selected these perform no
refcounting, and this should only be a structural change that does not
affect behaviour.

Acked-by: default avatarMark Rutland <mark.rutland@arm.com>
Signed-off-by: default avatarChristophe Leroy <christophe.leroy@c-s.fr>
Reviewed-by: default avatarNicholas Piggin <npiggin@gmail.com>
[mpe: Move try_get_task_stack() below tsk == NULL check in show_stack()]
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
parent 05486089
Loading
Loading
Loading
Loading
+23 −3
Original line number Diff line number Diff line
@@ -2027,7 +2027,7 @@ int validate_sp(unsigned long sp, struct task_struct *p,

EXPORT_SYMBOL(validate_sp);

unsigned long get_wchan(struct task_struct *p)
static unsigned long __get_wchan(struct task_struct *p)
{
	unsigned long ip, sp;
	int count = 0;
@@ -2053,6 +2053,20 @@ unsigned long get_wchan(struct task_struct *p)
	return 0;
}

unsigned long get_wchan(struct task_struct *p)
{
	unsigned long ret;

	if (!try_get_task_stack(p))
		return 0;

	ret = __get_wchan(p);

	put_task_stack(p);

	return ret;
}

static int kstack_depth_to_print = CONFIG_PRINT_STACK_DEPTH;

void show_stack(struct task_struct *tsk, unsigned long *stack)
@@ -2067,9 +2081,13 @@ void show_stack(struct task_struct *tsk, unsigned long *stack)
	int curr_frame = 0;
#endif

	sp = (unsigned long) stack;
	if (tsk == NULL)
		tsk = current;

	if (!try_get_task_stack(tsk))
		return;

	sp = (unsigned long) stack;
	if (sp == 0) {
		if (tsk == current)
			sp = current_stack_pointer();
@@ -2081,7 +2099,7 @@ void show_stack(struct task_struct *tsk, unsigned long *stack)
	printk("Call Trace:\n");
	do {
		if (!validate_sp(sp, tsk, STACK_FRAME_OVERHEAD))
			return;
			break;

		stack = (unsigned long *) sp;
		newsp = stack[0];
@@ -2121,6 +2139,8 @@ void show_stack(struct task_struct *tsk, unsigned long *stack)

		sp = newsp;
	} while (count++ < kstack_depth_to_print);

	put_task_stack(tsk);
}

#ifdef CONFIG_PPC64
+26 −3
Original line number Diff line number Diff line
@@ -67,12 +67,17 @@ void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
{
	unsigned long sp;

	if (!try_get_task_stack(tsk))
		return;

	if (tsk == current)
		sp = current_stack_pointer();
	else
		sp = tsk->thread.ksp;

	save_context_stack(trace, sp, tsk, 0);

	put_task_stack(tsk);
}
EXPORT_SYMBOL_GPL(save_stack_trace_tsk);

@@ -90,8 +95,7 @@ EXPORT_SYMBOL_GPL(save_stack_trace_regs);
 *
 * If the task is not 'current', the caller *must* ensure the task is inactive.
 */
int
save_stack_trace_tsk_reliable(struct task_struct *tsk,
static int __save_stack_trace_tsk_reliable(struct task_struct *tsk,
					   struct stack_trace *trace)
{
	unsigned long sp;
@@ -197,6 +201,25 @@ save_stack_trace_tsk_reliable(struct task_struct *tsk,
	}
	return 0;
}

int save_stack_trace_tsk_reliable(struct task_struct *tsk,
				  struct stack_trace *trace)
{
	int ret;

	/*
	 * If the task doesn't have a stack (e.g., a zombie), the stack is
	 * "reliably" empty.
	 */
	if (!try_get_task_stack(tsk))
		return 0;

	ret = __save_stack_trace_tsk_reliable(tsk, trace);

	put_task_stack(tsk);

	return ret;
}
EXPORT_SYMBOL_GPL(save_stack_trace_tsk_reliable);
#endif /* CONFIG_HAVE_RELIABLE_STACKTRACE */