Revert 337f13046ff0 ("futex: Allow FUTEX_CLOCK_REALTIME with FUTEX_WAIT op")
[linux-2.6-microblaze.git] / kernel / signal.c
index e528f96..66e8864 100644 (file)
@@ -408,7 +408,8 @@ void task_join_group_stop(struct task_struct *task)
  *   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_alloc(int sig, struct task_struct *t, gfp_t gfp_flags,
+                int override_rlimit, const unsigned int sigqueue_flags)
 {
        struct sigqueue *q = NULL;
        struct user_struct *user;
@@ -430,7 +431,16 @@ __sigqueue_alloc(int sig, struct task_struct *t, gfp_t flags, int override_rlimi
        rcu_read_unlock();
 
        if (override_rlimit || likely(sigpending <= task_rlimit(t, RLIMIT_SIGPENDING))) {
-               q = kmem_cache_alloc(sigqueue_cachep, flags);
+               /*
+                * Preallocation does not hold sighand::siglock so it can't
+                * use the cache. The lockless caching requires that only
+                * one consumer and only one producer run at a time.
+                */
+               q = READ_ONCE(t->sigqueue_cache);
+               if (!q || sigqueue_flags)
+                       q = kmem_cache_alloc(sigqueue_cachep, gfp_flags);
+               else
+                       WRITE_ONCE(t->sigqueue_cache, NULL);
        } else {
                print_dropped_signal(sig);
        }
@@ -440,20 +450,51 @@ __sigqueue_alloc(int sig, struct task_struct *t, gfp_t flags, int override_rlimi
                        free_uid(user);
        } else {
                INIT_LIST_HEAD(&q->list);
-               q->flags = 0;
+               q->flags = sigqueue_flags;
                q->user = user;
        }
 
        return q;
 }
 
+void exit_task_sigqueue_cache(struct task_struct *tsk)
+{
+       /* Race free because @tsk is mopped up */
+       struct sigqueue *q = tsk->sigqueue_cache;
+
+       if (q) {
+               tsk->sigqueue_cache = NULL;
+               /*
+                * Hand it back to the cache as the task might
+                * be self reaping which would leak the object.
+                */
+                kmem_cache_free(sigqueue_cachep, q);
+       }
+}
+
+static void sigqueue_cache_or_free(struct sigqueue *q)
+{
+       /*
+        * Cache one sigqueue per task. This pairs with the consumer side
+        * in __sigqueue_alloc() and needs READ/WRITE_ONCE() to prevent the
+        * compiler from store tearing and to tell KCSAN that the data race
+        * is intentional when run without holding current->sighand->siglock,
+        * which is fine as current obviously cannot run __sigqueue_free()
+        * concurrently.
+        */
+       if (!READ_ONCE(current->sigqueue_cache))
+               WRITE_ONCE(current->sigqueue_cache, q);
+       else
+               kmem_cache_free(sigqueue_cachep, q);
+}
+
 static void __sigqueue_free(struct sigqueue *q)
 {
        if (q->flags & SIGQUEUE_PREALLOC)
                return;
        if (atomic_dec_and_test(&q->user->sigpending))
                free_uid(q->user);
-       kmem_cache_free(sigqueue_cachep, q);
+       sigqueue_cache_or_free(q);
 }
 
 void flush_sigqueue(struct sigpending *queue)
@@ -1111,7 +1152,8 @@ static int __send_signal(int sig, struct kernel_siginfo *info, struct task_struc
        else
                override_rlimit = 0;
 
-       q = __sigqueue_alloc(sig, t, GFP_ATOMIC, override_rlimit);
+       q = __sigqueue_alloc(sig, t, GFP_ATOMIC, override_rlimit, 0);
+
        if (q) {
                list_add_tail(&q->list, &pending->list);
                switch ((unsigned long) info) {
@@ -1197,6 +1239,7 @@ static inline bool has_si_pid_and_uid(struct kernel_siginfo *info)
        case SIL_FAULT_MCEERR:
        case SIL_FAULT_BNDERR:
        case SIL_FAULT_PKUERR:
+       case SIL_PERF_EVENT:
        case SIL_SYS:
                ret = false;
                break;
@@ -1805,12 +1848,7 @@ EXPORT_SYMBOL(kill_pid);
  */
 struct sigqueue *sigqueue_alloc(void)
 {
-       struct sigqueue *q = __sigqueue_alloc(-1, current, GFP_KERNEL, 0);
-
-       if (q)
-               q->flags |= SIGQUEUE_PREALLOC;
-
-       return q;
+       return __sigqueue_alloc(-1, current, GFP_KERNEL, 0, SIGQUEUE_PREALLOC);
 }
 
 void sigqueue_free(struct sigqueue *q)
@@ -2529,6 +2567,7 @@ static void hide_si_addr_tag_bits(struct ksignal *ksig)
        case SIL_FAULT_MCEERR:
        case SIL_FAULT_BNDERR:
        case SIL_FAULT_PKUERR:
+       case SIL_PERF_EVENT:
                ksig->info.si_addr = arch_untagged_si_addr(
                        ksig->info.si_addr, ksig->sig, ksig->info.si_code);
                break;
@@ -3210,6 +3249,8 @@ enum siginfo_layout siginfo_layout(unsigned sig, int si_code)
                        else if ((sig == SIGSEGV) && (si_code == SEGV_PKUERR))
                                layout = SIL_FAULT_PKUERR;
 #endif
+                       else if ((sig == SIGTRAP) && (si_code == TRAP_PERF))
+                               layout = SIL_PERF_EVENT;
                }
                else if (si_code <= NSIGPOLL)
                        layout = SIL_POLL;
@@ -3339,6 +3380,10 @@ void copy_siginfo_to_external32(struct compat_siginfo *to,
 #endif
                to->si_pkey = from->si_pkey;
                break;
+       case SIL_PERF_EVENT:
+               to->si_addr = ptr_to_compat(from->si_addr);
+               to->si_perf = from->si_perf;
+               break;
        case SIL_CHLD:
                to->si_pid = from->si_pid;
                to->si_uid = from->si_uid;
@@ -3419,6 +3464,10 @@ static int post_copy_siginfo_from_user32(kernel_siginfo_t *to,
 #endif
                to->si_pkey = from->si_pkey;
                break;
+       case SIL_PERF_EVENT:
+               to->si_addr = compat_ptr(from->si_addr);
+               to->si_perf = from->si_perf;
+               break;
        case SIL_CHLD:
                to->si_pid    = from->si_pid;
                to->si_uid    = from->si_uid;
@@ -4599,6 +4648,7 @@ static inline void siginfo_buildtime_checks(void)
        CHECK_OFFSET(si_lower);
        CHECK_OFFSET(si_upper);
        CHECK_OFFSET(si_pkey);
+       CHECK_OFFSET(si_perf);
 
        /* sigpoll */
        CHECK_OFFSET(si_band);