]> 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 cd83cc3767670f6fbe43577240922652df66d870..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.