lkml.org 
[lkml]   [2018]   [Jul]   [24]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Date
    Subject[PATCH 19/20] fork: Have new threads join on-going signal group stops
    There are only two signals that are delivered to every member of a
    signal group: SIGSTOP and SIGKILL. Signal delivery requires every
    signal appear to be delivered either before or after a clone syscall.
    SIGKILL terminates the clone so does not need to be considered. Which
    leaves only SIGSTOP that needs to be considered when creating new
    threads.

    Today in the event of a group stop TIF_SIGPENDING will get set and the
    fork will restart ensuring the fork syscall participates in the group
    stop.

    A fork (especially of a process with a lot of memory) is one of the
    most expensive system so we really only want to restart a fork when
    necessary.

    It is easy so check to see if a SIGSTOP is ongoing have have the new
    thread join it immediate after the clone completes. Making it appear
    the clone completed happened just before the SIGSTOP.

    The calculate_sigpending function will see the bits set in jobctl and
    set TIF_SIGPENDING to ensure the new task takes the slow path to userspace.

    Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
    ---
    include/linux/sched/signal.h | 2 ++
    kernel/fork.c | 27 +++++++++++++++------------
    kernel/signal.c | 14 ++++++++++++++
    3 files changed, 31 insertions(+), 12 deletions(-)

    diff --git a/include/linux/sched/signal.h b/include/linux/sched/signal.h
    index 7cabc0bc38f6..f3507bf165d0 100644
    --- a/include/linux/sched/signal.h
    +++ b/include/linux/sched/signal.h
    @@ -385,6 +385,8 @@ static inline void ptrace_signal_wake_up(struct task_struct *t, bool resume)
    signal_wake_up_state(t, resume ? __TASK_TRACED : 0);
    }

    +void task_join_group_stop(struct task_struct *task);
    +
    #ifdef TIF_RESTORE_SIGMASK
    /*
    * Legacy restore_sigmask accessors. These are inefficient on
    diff --git a/kernel/fork.c b/kernel/fork.c
    index e07281254552..6c358846a8b8 100644
    --- a/kernel/fork.c
    +++ b/kernel/fork.c
    @@ -1934,18 +1934,20 @@ static __latent_entropy struct task_struct *copy_process(
    goto bad_fork_cancel_cgroup;
    }

    - /*
    - * Process group and session signals need to be delivered to just the
    - * parent before the fork or both the parent and the child after the
    - * fork. Restart if a signal comes in before we add the new process to
    - * it's process group.
    - * A fatal signal pending means that current will exit, so the new
    - * thread can't slip out of an OOM kill (or normal SIGKILL).
    - */
    - recalc_sigpending();
    - if (signal_pending(current)) {
    - retval = -ERESTARTNOINTR;
    - goto bad_fork_cancel_cgroup;
    + if (!(clone_flags & CLONE_THREAD)) {
    + /*
    + * Process group and session signals need to be delivered to just the
    + * parent before the fork or both the parent and the child after the
    + * fork. Restart if a signal comes in before we add the new process to
    + * it's process group.
    + * A fatal signal pending means that current will exit, so the new
    + * thread can't slip out of an OOM kill (or normal SIGKILL).
    + */
    + recalc_sigpending();
    + if (signal_pending(current)) {
    + retval = -ERESTARTNOINTR;
    + goto bad_fork_cancel_cgroup;
    + }
    }


    @@ -1986,6 +1988,7 @@ static __latent_entropy struct task_struct *copy_process(
    &p->group_leader->thread_group);
    list_add_tail_rcu(&p->thread_node,
    &p->signal->thread_head);
    + task_join_group_stop(p);
    }
    attach_pid(p, PIDTYPE_PID);
    calculate_sigpending(p);
    diff --git a/kernel/signal.c b/kernel/signal.c
    index f6687c7d7a8c..78e2d5d196f3 100644
    --- a/kernel/signal.c
    +++ b/kernel/signal.c
    @@ -375,6 +375,20 @@ static bool task_participate_group_stop(struct task_struct *task)
    return false;
    }

    +void task_join_group_stop(struct task_struct *task)
    +{
    + /* Have the new thread join an on-going signal group stop */
    + unsigned long jobctl = current->jobctl;
    + if (jobctl & JOBCTL_STOP_PENDING) {
    + struct signal_struct *sig = current->signal;
    + unsigned long signr = jobctl & JOBCTL_STOP_SIGMASK;
    + unsigned long gstop = JOBCTL_STOP_PENDING | JOBCTL_STOP_CONSUME;
    + if (task_set_jobctl_pending(task, signr | gstop)) {
    + sig->group_stop_count++;
    + }
    + }
    +}
    +
    /*
    * allocate a new signal queue record
    * - this may be called without locks if and only if t == current, otherwise an
    --
    2.17.1
    \
     
     \ /
      Last update: 2018-07-24 05:28    [W:4.171 / U:0.236 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site