]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - kernel/signal.c
Merge branch 'fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[linux.git] / kernel / signal.c
index 227ba170298e5b457c9b405c5376c466fe26850b..62f9aea4a15a0f6295145626ee5597970785d7d3 100644 (file)
@@ -43,6 +43,7 @@
 #include <linux/compiler.h>
 #include <linux/posix-timers.h>
 #include <linux/livepatch.h>
+#include <linux/cgroup.h>
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/signal.h>
@@ -146,9 +147,10 @@ static inline bool has_pending_signals(sigset_t *signal, sigset_t *blocked)
 
 static bool recalc_sigpending_tsk(struct task_struct *t)
 {
-       if ((t->jobctl & JOBCTL_PENDING_MASK) ||
+       if ((t->jobctl & (JOBCTL_PENDING_MASK | JOBCTL_TRAP_FREEZE)) ||
            PENDING(&t->pending, &t->blocked) ||
-           PENDING(&t->signal->shared_pending, &t->blocked)) {
+           PENDING(&t->signal->shared_pending, &t->blocked) ||
+           cgroup_task_frozen(t)) {
                set_tsk_thread_flag(t, TIF_SIGPENDING);
                return true;
        }
@@ -2108,6 +2110,7 @@ static void ptrace_stop(int exit_code, int why, int clear_code, kernel_siginfo_t
                preempt_disable();
                read_unlock(&tasklist_lock);
                preempt_enable_no_resched();
+               cgroup_enter_frozen();
                freezable_schedule();
        } else {
                /*
@@ -2286,6 +2289,7 @@ static bool do_signal_stop(int signr)
                }
 
                /* Now we don't run again until woken by SIGCONT or SIGKILL */
+               cgroup_enter_frozen();
                freezable_schedule();
                return true;
        } else {
@@ -2332,6 +2336,43 @@ static void do_jobctl_trap(void)
        }
 }
 
+/**
+ * do_freezer_trap - handle the freezer jobctl trap
+ *
+ * Puts the task into frozen state, if only the task is not about to quit.
+ * In this case it drops JOBCTL_TRAP_FREEZE.
+ *
+ * CONTEXT:
+ * Must be called with @current->sighand->siglock held,
+ * which is always released before returning.
+ */
+static void do_freezer_trap(void)
+       __releases(&current->sighand->siglock)
+{
+       /*
+        * If there are other trap bits pending except JOBCTL_TRAP_FREEZE,
+        * let's make another loop to give it a chance to be handled.
+        * In any case, we'll return back.
+        */
+       if ((current->jobctl & (JOBCTL_PENDING_MASK | JOBCTL_TRAP_FREEZE)) !=
+            JOBCTL_TRAP_FREEZE) {
+               spin_unlock_irq(&current->sighand->siglock);
+               return;
+       }
+
+       /*
+        * Now we're sure that there is no pending fatal signal and no
+        * pending traps. Clear TIF_SIGPENDING to not get out of schedule()
+        * immediately (if there is a non-fatal signal pending), and
+        * put the task into sleep.
+        */
+       __set_current_state(TASK_INTERRUPTIBLE);
+       clear_thread_flag(TIF_SIGPENDING);
+       spin_unlock_irq(&current->sighand->siglock);
+       cgroup_enter_frozen();
+       freezable_schedule();
+}
+
 static int ptrace_signal(int signr, kernel_siginfo_t *info)
 {
        /*
@@ -2452,9 +2493,24 @@ bool get_signal(struct ksignal *ksig)
                    do_signal_stop(0))
                        goto relock;
 
-               if (unlikely(current->jobctl & JOBCTL_TRAP_MASK)) {
-                       do_jobctl_trap();
+               if (unlikely(current->jobctl &
+                            (JOBCTL_TRAP_MASK | JOBCTL_TRAP_FREEZE))) {
+                       if (current->jobctl & JOBCTL_TRAP_MASK) {
+                               do_jobctl_trap();
+                               spin_unlock_irq(&sighand->siglock);
+                       } else if (current->jobctl & JOBCTL_TRAP_FREEZE)
+                               do_freezer_trap();
+
+                       goto relock;
+               }
+
+               /*
+                * If the task is leaving the frozen state, let's update
+                * cgroup counters and reset the frozen bit.
+                */
+               if (unlikely(cgroup_task_frozen(current))) {
                        spin_unlock_irq(&sighand->siglock);
+                       cgroup_leave_frozen(false);
                        goto relock;
                }
 
@@ -2550,6 +2606,8 @@ bool get_signal(struct ksignal *ksig)
 
        fatal:
                spin_unlock_irq(&sighand->siglock);
+               if (unlikely(cgroup_task_frozen(current)))
+                       cgroup_leave_frozen(true);
 
                /*
                 * Anything else is fatal, maybe with a core dump.
@@ -3513,7 +3571,6 @@ SYSCALL_DEFINE2(kill, pid_t, pid, int, sig)
        return kill_something_info(sig, &info, pid);
 }
 
-#ifdef CONFIG_PROC_FS
 /*
  * Verify that the signaler and signalee either are in the same pid namespace
  * or that the signaler's pid namespace is an ancestor of the signalee's pid
@@ -3550,6 +3607,14 @@ static int copy_siginfo_from_user_any(kernel_siginfo_t *kinfo, siginfo_t *info)
        return copy_siginfo_from_user(kinfo, info);
 }
 
+static struct pid *pidfd_to_pid(const struct file *file)
+{
+       if (file->f_op == &pidfd_fops)
+               return file->private_data;
+
+       return tgid_pidfd_to_pid(file);
+}
+
 /**
  * sys_pidfd_send_signal - send a signal to a process through a task file
  *                          descriptor
@@ -3586,7 +3651,7 @@ SYSCALL_DEFINE4(pidfd_send_signal, int, pidfd, int, sig,
                return -EBADF;
 
        /* Is this a pidfd? */
-       pid = tgid_pidfd_to_pid(f.file);
+       pid = pidfd_to_pid(f.file);
        if (IS_ERR(pid)) {
                ret = PTR_ERR(pid);
                goto err;
@@ -3620,7 +3685,6 @@ SYSCALL_DEFINE4(pidfd_send_signal, int, pidfd, int, sig,
        fdput(f);
        return ret;
 }
-#endif /* CONFIG_PROC_FS */
 
 static int
 do_send_specific(pid_t tgid, pid_t pid, int sig, struct kernel_siginfo *info)