Re: PATCH? tracehook_notify_jctl && SIGCONT

2009-03-19 Thread Roland McGrath
> Now we send SIGCHLD with si_code = CLD_STOPPED twice. By T1 from
> do_signal_stop(), and by T1 or T2 from get_signal_to_deliver() which
> checks SIGNAL_CLD_MASK.

Yes, I considered this problem.  It's just not so big a deal as to worry
overmuch about this corner case in the first version.  What seems to me
will be the obvious and straightforward way to address it is to give
utrace_report_jctl() a return value that tracehook_notify_jctl() uses.
Then we can omit a notification that has been superceded.

Your patch does not seem very straightforward to me.  Moreover, you moved
some ptrace magic out of the tracehook function back into core signals code.
That is going in the wrong direction and we won't have any of that.


Thanks,
Roland



PATCH? tracehook_notify_jctl && SIGCONT

2009-03-18 Thread Oleg Nesterov
On 03/18, Oleg Nesterov wrote:
>
> On 03/18, Roland McGrath wrote:
> >
> > It's OK to change the tracehook definition.  I did this on the new git
> > branch tracehook, then utrace branch commit 7b0be6e4 merges that and uses 
> > it.
>
> Roland, I think it better to change tracehook definition more, please
> see below.

The problem is that, since utrace_report_jctl() drops ->siglock,
tracehook_notify_jctl() can return false positive. This is easy
to fix, but then we have to check PT_PTRACED twice, not good.

Suppose we have 2 threads, T1 and T2, T1 has JCTL in ->utrace_flags.

T2 dequeues SIGSTOP, calls do_signal_stop(), and sleeps in TASK_STOPPED.

T1 calls do_signal_stop(). ->group_stop_count == 1, so it does
notify = tracehook_notify_jctl(notify => CLD_STOPPED), this means
that notify always becomes CLD_STOPPED.

When tracehook_notify_jctl()->utrace_notify_jctl() drops siglock,
SIGCONT comes, notices ->group_stop_count != 0, and adds SIGNAL_CLD_STOPPED
to signal flags.

Now we send SIGCHLD with si_code = CLD_STOPPED twice. By T1 from
do_signal_stop(), and by T1 or T2 from get_signal_to_deliver() which
checks SIGNAL_CLD_MASK.

I'd suggest something like the patch below. At least for now.

Oleg.

--- xxx/include/linux/tracehook.h~JCTL_NOTIFY   2009-03-18 14:50:05.0 
+0100
+++ xxx/include/linux/tracehook.h   2009-03-18 20:18:54.0 +0100
@@ -520,11 +520,10 @@ static inline int tracehook_get_signal(s
  *
  * Called with the siglock held.
  */
-static inline int tracehook_notify_jctl(int notify, int why)
+static inline void tracehook_notify_jctl(int notify, int why)
 {
if (task_utrace_flags(current) & UTRACE_EVENT(JCTL))
utrace_report_jctl(notify, why);
-   return notify ?: (current->ptrace & PT_PTRACED) ? why : 0;
 }
 
 #define DEATH_REAP -1
--- xxx/kernel/signal.c~JCTL_NOTIFY 2009-03-18 18:20:35.0 +0100
+++ xxx/kernel/signal.c 2009-03-18 20:28:39.0 +0100
@@ -1671,18 +1671,21 @@ static int do_signal_stop(int signr)
 * a group stop in progress and we are the last to stop,
 * report to the parent.  When ptraced, every thread reports itself.
 */
-   notify = sig->group_stop_count == 1 ? CLD_STOPPED : 0;
-   notify = tracehook_notify_jctl(notify, CLD_STOPPED);
+   tracehook_notify_jctl(sig->group_stop_count == 1 ? CLD_STOPPED : 0,
+   CLD_STOPPED);
 
+   notify = 0;
if (sig->group_stop_count) {
-   if (!--sig->group_stop_count)
+   if (!--sig->group_stop_count) {
sig->flags = SIGNAL_STOP_STOPPED;
+   notify = 1;
+   }
current->exit_code = sig->group_exit_code;
__set_current_state(TASK_STOPPED);
}
spin_unlock_irq(¤t->sighand->siglock);
 
-   if (notify) {
+   if (notify || (current->ptrace & PT_PTRACED)) {
read_lock(&tasklist_lock);
do_notify_parent_cldstop(current, notify);
read_unlock(&tasklist_lock);
@@ -1765,14 +1768,12 @@ relock:
? CLD_CONTINUED : CLD_STOPPED;
signal->flags &= ~SIGNAL_CLD_MASK;
 
-   why = tracehook_notify_jctl(why, CLD_CONTINUED);
+   tracehook_notify_jctl(why, CLD_CONTINUED);
spin_unlock_irq(&sighand->siglock);
 
-   if (why) {
-   read_lock(&tasklist_lock);
-   do_notify_parent_cldstop(current->group_leader, why);
-   read_unlock(&tasklist_lock);
-   }
+   read_lock(&tasklist_lock);
+   do_notify_parent_cldstop(current->group_leader, why);
+   read_unlock(&tasklist_lock);
goto relock;
}
 
@@ -1930,7 +1931,8 @@ void exit_signals(struct task_struct *ts
if (unlikely(tsk->signal->group_stop_count) &&
!--tsk->signal->group_stop_count) {
tsk->signal->flags = SIGNAL_STOP_STOPPED;
-   group_stop = tracehook_notify_jctl(CLD_STOPPED, CLD_STOPPED);
+   tracehook_notify_jctl(CLD_STOPPED, CLD_STOPPED);
+   group_stop = 1;
}
 out:
spin_unlock_irq(&tsk->sighand->siglock);