]> rtime.felk.cvut.cz Git - zynq/linux.git/blobdiff - kernel/signal.c
Apply preempt_rt patch-4.9-rt1.patch.xz
[zynq/linux.git] / kernel / signal.c
index 75761acc77cf746798d0cd84fe7cd3086d70d6cb..ae0773c76bb077f2bc6672a68e9ec5bb2ff6a9a7 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/export.h>
 #include <linux/init.h>
 #include <linux/sched.h>
+#include <linux/sched/rt.h>
 #include <linux/fs.h>
 #include <linux/tty.h>
 #include <linux/binfmts.h>
@@ -352,13 +353,30 @@ static bool task_participate_group_stop(struct task_struct *task)
        return false;
 }
 
+static inline struct sigqueue *get_task_cache(struct task_struct *t)
+{
+       struct sigqueue *q = t->sigqueue_cache;
+
+       if (cmpxchg(&t->sigqueue_cache, q, NULL) != q)
+               return NULL;
+       return q;
+}
+
+static inline int put_task_cache(struct task_struct *t, struct sigqueue *q)
+{
+       if (cmpxchg(&t->sigqueue_cache, NULL, q) == NULL)
+               return 0;
+       return 1;
+}
+
 /*
  * allocate a new signal queue record
  * - this may be called without locks if and only if t == current, otherwise an
  *   appropriate lock must be held to stop the target task from exiting
  */
 static struct sigqueue *
-__sigqueue_alloc(int sig, struct task_struct *t, gfp_t flags, int override_rlimit)
+__sigqueue_do_alloc(int sig, struct task_struct *t, gfp_t flags,
+                   int override_rlimit, int fromslab)
 {
        struct sigqueue *q = NULL;
        struct user_struct *user;
@@ -375,7 +393,10 @@ __sigqueue_alloc(int sig, struct task_struct *t, gfp_t flags, int override_rlimi
        if (override_rlimit ||
            atomic_read(&user->sigpending) <=
                        task_rlimit(t, RLIMIT_SIGPENDING)) {
-               q = kmem_cache_alloc(sigqueue_cachep, flags);
+               if (!fromslab)
+                       q = get_task_cache(t);
+               if (!q)
+                       q = kmem_cache_alloc(sigqueue_cachep, flags);
        } else {
                print_dropped_signal(sig);
        }
@@ -392,6 +413,13 @@ __sigqueue_alloc(int sig, struct task_struct *t, gfp_t flags, int override_rlimi
        return q;
 }
 
+static struct sigqueue *
+__sigqueue_alloc(int sig, struct task_struct *t, gfp_t flags,
+                int override_rlimit)
+{
+       return __sigqueue_do_alloc(sig, t, flags, override_rlimit, 0);
+}
+
 static void __sigqueue_free(struct sigqueue *q)
 {
        if (q->flags & SIGQUEUE_PREALLOC)
@@ -401,6 +429,21 @@ static void __sigqueue_free(struct sigqueue *q)
        kmem_cache_free(sigqueue_cachep, q);
 }
 
+static void sigqueue_free_current(struct sigqueue *q)
+{
+       struct user_struct *up;
+
+       if (q->flags & SIGQUEUE_PREALLOC)
+               return;
+
+       up = q->user;
+       if (rt_prio(current->normal_prio) && !put_task_cache(current, q)) {
+               atomic_dec(&up->sigpending);
+               free_uid(up);
+       } else
+                 __sigqueue_free(q);
+}
+
 void flush_sigqueue(struct sigpending *queue)
 {
        struct sigqueue *q;
@@ -413,6 +456,21 @@ void flush_sigqueue(struct sigpending *queue)
        }
 }
 
+/*
+ * Called from __exit_signal. Flush tsk->pending and
+ * tsk->sigqueue_cache
+ */
+void flush_task_sigqueue(struct task_struct *tsk)
+{
+       struct sigqueue *q;
+
+       flush_sigqueue(&tsk->pending);
+
+       q = get_task_cache(tsk);
+       if (q)
+               kmem_cache_free(sigqueue_cachep, q);
+}
+
 /*
  * Flush all pending signals for this kthread.
  */
@@ -525,7 +583,7 @@ static void collect_signal(int sig, struct sigpending *list, siginfo_t *info)
 still_pending:
                list_del_init(&first->list);
                copy_siginfo(info, &first->info);
-               __sigqueue_free(first);
+               sigqueue_free_current(first);
        } else {
                /*
                 * Ok, it wasn't in the queue.  This must be
@@ -560,6 +618,8 @@ int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info)
 {
        int signr;
 
+       WARN_ON_ONCE(tsk != current);
+
        /* We only dequeue private signals from ourselves, we don't let
         * signalfd steal them
         */
@@ -1156,8 +1216,8 @@ int do_send_sig_info(int sig, struct siginfo *info, struct task_struct *p,
  * We don't want to have recursive SIGSEGV's etc, for example,
  * that is why we also clear SIGNAL_UNKILLABLE.
  */
-int
-force_sig_info(int sig, struct siginfo *info, struct task_struct *t)
+static int
+do_force_sig_info(int sig, struct siginfo *info, struct task_struct *t)
 {
        unsigned long int flags;
        int ret, blocked, ignored;
@@ -1182,6 +1242,39 @@ force_sig_info(int sig, struct siginfo *info, struct task_struct *t)
        return ret;
 }
 
+int force_sig_info(int sig, struct siginfo *info, struct task_struct *t)
+{
+/*
+ * On some archs, PREEMPT_RT has to delay sending a signal from a trap
+ * since it can not enable preemption, and the signal code's spin_locks
+ * turn into mutexes. Instead, it must set TIF_NOTIFY_RESUME which will
+ * send the signal on exit of the trap.
+ */
+#ifdef ARCH_RT_DELAYS_SIGNAL_SEND
+       if (in_atomic()) {
+               if (WARN_ON_ONCE(t != current))
+                       return 0;
+               if (WARN_ON_ONCE(t->forced_info.si_signo))
+                       return 0;
+
+               if (is_si_special(info)) {
+                       WARN_ON_ONCE(info != SEND_SIG_PRIV);
+                       t->forced_info.si_signo = sig;
+                       t->forced_info.si_errno = 0;
+                       t->forced_info.si_code = SI_KERNEL;
+                       t->forced_info.si_pid = 0;
+                       t->forced_info.si_uid = 0;
+               } else {
+                       t->forced_info = *info;
+               }
+
+               set_tsk_thread_flag(t, TIF_NOTIFY_RESUME);
+               return 0;
+       }
+#endif
+       return do_force_sig_info(sig, info, t);
+}
+
 /*
  * Nuke all other threads in the group.
  */
@@ -1216,12 +1309,12 @@ struct sighand_struct *__lock_task_sighand(struct task_struct *tsk,
                 * Disable interrupts early to avoid deadlocks.
                 * See rcu_read_unlock() comment header for details.
                 */
-               local_irq_save(*flags);
+               local_irq_save_nort(*flags);
                rcu_read_lock();
                sighand = rcu_dereference(tsk->sighand);
                if (unlikely(sighand == NULL)) {
                        rcu_read_unlock();
-                       local_irq_restore(*flags);
+                       local_irq_restore_nort(*flags);
                        break;
                }
                /*
@@ -1242,7 +1335,7 @@ struct sighand_struct *__lock_task_sighand(struct task_struct *tsk,
                }
                spin_unlock(&sighand->siglock);
                rcu_read_unlock();
-               local_irq_restore(*flags);
+               local_irq_restore_nort(*flags);
        }
 
        return sighand;
@@ -1485,7 +1578,8 @@ EXPORT_SYMBOL(kill_pid);
  */
 struct sigqueue *sigqueue_alloc(void)
 {
-       struct sigqueue *q = __sigqueue_alloc(-1, current, GFP_KERNEL, 0);
+       /* Preallocated sigqueue objects always from the slabcache ! */
+       struct sigqueue *q = __sigqueue_do_alloc(-1, current, GFP_KERNEL, 0, 1);
 
        if (q)
                q->flags |= SIGQUEUE_PREALLOC;
@@ -1846,15 +1940,7 @@ static void ptrace_stop(int exit_code, int why, int clear_code, siginfo_t *info)
                if (gstop_done && ptrace_reparented(current))
                        do_notify_parent_cldstop(current, false, why);
 
-               /*
-                * Don't want to allow preemption here, because
-                * sys_ptrace() needs this task to be inactive.
-                *
-                * XXX: implement read_unlock_no_resched().
-                */
-               preempt_disable();
                read_unlock(&tasklist_lock);
-               preempt_enable_no_resched();
                freezable_schedule();
        } else {
                /*