[PATCH] Fix for WCHAN ps output in 2.2.0-pre9

Stephen C. Tweedie (sct@redhat.com)
Thu, 21 Jan 1999 11:34:57 GMT


Hi,

Wchan was broken in 2.2.0pre8 by the mm locking fixes in
fs/proc/array.c.

The problem is that we now snapshot the task_struct when decoding it,
and so the copy we are examining is at a different address from the
original process's task_struct. get_wchan() needs the address of that
original struct in order to get the correct address for the stack page
when validating the callback chain.

The fix is to allow grab_task() to return both the original task_struct
address and the new snapshot address, and to use the original pointer
when validating stack addresses in get_wchan(). Note that we still end
up looking up the old stack page as soon as we start following the
backtrace. There's not really anything we can do to make wchan
calculation atomic with respect to other processes which may be active
in kernel mode, but at least we abort the search if we go outside the
stack page so the only possible consequence is a transient invalid wchan
field.

The patch below is against 2.2.0-pre8, but applies and works against
2.2.0-pre9 too (although with offset line numbers when you apply it).

--Stephen

----------------------------------------------------------------
--- fs/proc/array.c~ Tue Jan 19 11:40:58 1999
+++ fs/proc/array.c Wed Jan 20 20:42:54 1999
@@ -512,7 +512,9 @@
#define first_sched ((unsigned long) scheduling_functions_start_here)
#define last_sched ((unsigned long) scheduling_functions_end_here)

-static unsigned long get_wchan(struct task_struct *p)
+/* p is the pointer to the usable snapshot of the task_struct.
+ * a is the real address of the struct in core. */
+static unsigned long get_wchan(struct task_struct *p, struct task_struct *a)
{
if (!p || p == current || p->state == TASK_RUNNING)
return 0;
@@ -522,7 +524,7 @@
unsigned long stack_page;
int count = 0;

- stack_page = (unsigned long)p;
+ stack_page = (unsigned long)a;
esp = p->tss.esp;
if (!stack_page || esp < stack_page || esp >= 8188+stack_page)
return 0;
@@ -849,13 +851,17 @@
cap_t(p->cap_effective));
}

-static struct task_struct *grab_task(int pid, struct task_struct *dst)
+static struct task_struct *grab_task(int pid,
+ struct task_struct *dst,
+ struct task_struct **real_dst)
{
struct task_struct *tsk = current;
if (pid != tsk->pid) {
read_lock(&tasklist_lock);
tsk = find_task_by_pid(pid);
if (tsk) {
+ if (real_dst)
+ *real_dst = tsk;
memcpy(dst, tsk, sizeof(struct task_struct));
tsk = dst;
if (tsk->mm && tsk->mm != &init_mm)
@@ -877,7 +883,7 @@
char * orig = buffer;
struct task_struct *tsk, mytask;

- tsk = grab_task(pid, &mytask);
+ tsk = grab_task(pid, &mytask, 0);
if (!tsk)
return 0;
buffer = task_name(tsk, buffer);
@@ -891,7 +897,7 @@

static int get_stat(int pid, char * buffer)
{
- struct task_struct *tsk, mytask;
+ struct task_struct *tsk, *real_tsk, mytask;
unsigned long vsize, eip, esp, wchan;
long priority, nice;
int tty_pgrp;
@@ -899,7 +905,7 @@
char state;
int res;

- tsk = grab_task(pid, &mytask);
+ tsk = grab_task(pid, &mytask, &real_tsk);
if (!tsk)
return 0;
state = *get_task_state(tsk);
@@ -917,7 +923,7 @@
esp = KSTK_ESP(tsk);
}

- wchan = get_wchan(tsk);
+ wchan = get_wchan(tsk, real_tsk);

collect_sigign_sigcatch(tsk, &sigign, &sigcatch);

@@ -1258,7 +1264,7 @@
struct task_struct * tsk, mytask;
int i, len;

- tsk = grab_task(pid, &mytask);
+ tsk = grab_task(pid, &mytask, 0);
if (!tsk)
return 0;

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/