Allen Martin | 685e0f8 | 2016-07-26 19:34:29 -0700 | [diff] [blame^] | 1 | From 123a6303ad9225d288c91d1c8a216abc1749c7be Mon Sep 17 00:00:00 2001 |
| 2 | From: Thomas Gleixner <tglx@linutronix.de> |
| 3 | Date: Fri, 3 Jul 2009 08:44:56 -0500 |
| 4 | Subject: [PATCH 048/317] signals: Allow rt tasks to cache one sigqueue struct |
| 5 | X-NVConfidentiality: public |
| 6 | |
| 7 | To avoid allocation allow rt tasks to cache one sigqueue struct in |
| 8 | task struct. |
| 9 | |
| 10 | Signed-off-by: Thomas Gleixner <tglx@linutronix.de> |
| 11 | Signed-off-by: Allen Martin <amartin@nvidia.com> |
| 12 | --- |
| 13 | include/linux/sched.h | 1 + |
| 14 | include/linux/signal.h | 1 + |
| 15 | kernel/exit.c | 2 +- |
| 16 | kernel/fork.c | 1 + |
| 17 | kernel/signal.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++--- |
| 18 | 5 files changed, 69 insertions(+), 5 deletions(-) |
| 19 | |
| 20 | diff --git a/include/linux/sched.h b/include/linux/sched.h |
| 21 | index 162126b65026..8eeea5633ca7 100644 |
| 22 | --- a/include/linux/sched.h |
| 23 | +++ b/include/linux/sched.h |
| 24 | @@ -1569,6 +1569,7 @@ struct task_struct { |
| 25 | /* signal handlers */ |
| 26 | struct signal_struct *signal; |
| 27 | struct sighand_struct *sighand; |
| 28 | + struct sigqueue *sigqueue_cache; |
| 29 | |
| 30 | sigset_t blocked, real_blocked; |
| 31 | sigset_t saved_sigmask; /* restored if set_restore_sigmask() was used */ |
| 32 | diff --git a/include/linux/signal.h b/include/linux/signal.h |
| 33 | index 92557bbce7e7..561a1283ee97 100644 |
| 34 | --- a/include/linux/signal.h |
| 35 | +++ b/include/linux/signal.h |
| 36 | @@ -218,6 +218,7 @@ static inline void init_sigpending(struct sigpending *sig) |
| 37 | } |
| 38 | |
| 39 | extern void flush_sigqueue(struct sigpending *queue); |
| 40 | +extern void flush_task_sigqueue(struct task_struct *tsk); |
| 41 | |
| 42 | /* Test if 'sig' is valid signal. Use this instead of testing _NSIG directly */ |
| 43 | static inline int valid_signal(unsigned long sig) |
| 44 | diff --git a/kernel/exit.c b/kernel/exit.c |
| 45 | index 07110c6020a0..73497e458e4a 100644 |
| 46 | --- a/kernel/exit.c |
| 47 | +++ b/kernel/exit.c |
| 48 | @@ -144,7 +144,7 @@ static void __exit_signal(struct task_struct *tsk) |
| 49 | * Do this under ->siglock, we can race with another thread |
| 50 | * doing sigqueue_free() if we have SIGQUEUE_PREALLOC signals. |
| 51 | */ |
| 52 | - flush_sigqueue(&tsk->pending); |
| 53 | + flush_task_sigqueue(tsk); |
| 54 | tsk->sighand = NULL; |
| 55 | spin_unlock(&sighand->siglock); |
| 56 | |
| 57 | diff --git a/kernel/fork.c b/kernel/fork.c |
| 58 | index d687bb11d559..7080e756bf11 100644 |
| 59 | --- a/kernel/fork.c |
| 60 | +++ b/kernel/fork.c |
| 61 | @@ -1344,6 +1344,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, |
| 62 | spin_lock_init(&p->alloc_lock); |
| 63 | |
| 64 | init_sigpending(&p->pending); |
| 65 | + p->sigqueue_cache = NULL; |
| 66 | |
| 67 | p->utime = p->stime = p->gtime = 0; |
| 68 | p->utimescaled = p->stimescaled = 0; |
| 69 | diff --git a/kernel/signal.c b/kernel/signal.c |
| 70 | index c1bac41be985..0e08feb3e54e 100644 |
| 71 | --- a/kernel/signal.c |
| 72 | +++ b/kernel/signal.c |
| 73 | @@ -14,6 +14,7 @@ |
| 74 | #include <linux/export.h> |
| 75 | #include <linux/init.h> |
| 76 | #include <linux/sched.h> |
| 77 | +#include <linux/sched/rt.h> |
| 78 | #include <linux/fs.h> |
| 79 | #include <linux/tty.h> |
| 80 | #include <linux/binfmts.h> |
| 81 | @@ -352,13 +353,30 @@ static bool task_participate_group_stop(struct task_struct *task) |
| 82 | return false; |
| 83 | } |
| 84 | |
| 85 | +static inline struct sigqueue *get_task_cache(struct task_struct *t) |
| 86 | +{ |
| 87 | + struct sigqueue *q = t->sigqueue_cache; |
| 88 | + |
| 89 | + if (cmpxchg(&t->sigqueue_cache, q, NULL) != q) |
| 90 | + return NULL; |
| 91 | + return q; |
| 92 | +} |
| 93 | + |
| 94 | +static inline int put_task_cache(struct task_struct *t, struct sigqueue *q) |
| 95 | +{ |
| 96 | + if (cmpxchg(&t->sigqueue_cache, NULL, q) == NULL) |
| 97 | + return 0; |
| 98 | + return 1; |
| 99 | +} |
| 100 | + |
| 101 | /* |
| 102 | * allocate a new signal queue record |
| 103 | * - this may be called without locks if and only if t == current, otherwise an |
| 104 | * appropriate lock must be held to stop the target task from exiting |
| 105 | */ |
| 106 | static struct sigqueue * |
| 107 | -__sigqueue_alloc(int sig, struct task_struct *t, gfp_t flags, int override_rlimit) |
| 108 | +__sigqueue_do_alloc(int sig, struct task_struct *t, gfp_t flags, |
| 109 | + int override_rlimit, int fromslab) |
| 110 | { |
| 111 | struct sigqueue *q = NULL; |
| 112 | struct user_struct *user; |
| 113 | @@ -375,7 +393,10 @@ __sigqueue_alloc(int sig, struct task_struct *t, gfp_t flags, int override_rlimi |
| 114 | if (override_rlimit || |
| 115 | atomic_read(&user->sigpending) <= |
| 116 | task_rlimit(t, RLIMIT_SIGPENDING)) { |
| 117 | - q = kmem_cache_alloc(sigqueue_cachep, flags); |
| 118 | + if (!fromslab) |
| 119 | + q = get_task_cache(t); |
| 120 | + if (!q) |
| 121 | + q = kmem_cache_alloc(sigqueue_cachep, flags); |
| 122 | } else { |
| 123 | print_dropped_signal(sig); |
| 124 | } |
| 125 | @@ -392,6 +413,13 @@ __sigqueue_alloc(int sig, struct task_struct *t, gfp_t flags, int override_rlimi |
| 126 | return q; |
| 127 | } |
| 128 | |
| 129 | +static struct sigqueue * |
| 130 | +__sigqueue_alloc(int sig, struct task_struct *t, gfp_t flags, |
| 131 | + int override_rlimit) |
| 132 | +{ |
| 133 | + return __sigqueue_do_alloc(sig, t, flags, override_rlimit, 0); |
| 134 | +} |
| 135 | + |
| 136 | static void __sigqueue_free(struct sigqueue *q) |
| 137 | { |
| 138 | if (q->flags & SIGQUEUE_PREALLOC) |
| 139 | @@ -401,6 +429,21 @@ static void __sigqueue_free(struct sigqueue *q) |
| 140 | kmem_cache_free(sigqueue_cachep, q); |
| 141 | } |
| 142 | |
| 143 | +static void sigqueue_free_current(struct sigqueue *q) |
| 144 | +{ |
| 145 | + struct user_struct *up; |
| 146 | + |
| 147 | + if (q->flags & SIGQUEUE_PREALLOC) |
| 148 | + return; |
| 149 | + |
| 150 | + up = q->user; |
| 151 | + if (rt_prio(current->normal_prio) && !put_task_cache(current, q)) { |
| 152 | + atomic_dec(&up->sigpending); |
| 153 | + free_uid(up); |
| 154 | + } else |
| 155 | + __sigqueue_free(q); |
| 156 | +} |
| 157 | + |
| 158 | void flush_sigqueue(struct sigpending *queue) |
| 159 | { |
| 160 | struct sigqueue *q; |
| 161 | @@ -414,6 +457,21 @@ void flush_sigqueue(struct sigpending *queue) |
| 162 | } |
| 163 | |
| 164 | /* |
| 165 | + * Called from __exit_signal. Flush tsk->pending and |
| 166 | + * tsk->sigqueue_cache |
| 167 | + */ |
| 168 | +void flush_task_sigqueue(struct task_struct *tsk) |
| 169 | +{ |
| 170 | + struct sigqueue *q; |
| 171 | + |
| 172 | + flush_sigqueue(&tsk->pending); |
| 173 | + |
| 174 | + q = get_task_cache(tsk); |
| 175 | + if (q) |
| 176 | + kmem_cache_free(sigqueue_cachep, q); |
| 177 | +} |
| 178 | + |
| 179 | +/* |
| 180 | * Flush all pending signals for this kthread. |
| 181 | */ |
| 182 | void flush_signals(struct task_struct *t) |
| 183 | @@ -525,7 +583,7 @@ static void collect_signal(int sig, struct sigpending *list, siginfo_t *info) |
| 184 | still_pending: |
| 185 | list_del_init(&first->list); |
| 186 | copy_siginfo(info, &first->info); |
| 187 | - __sigqueue_free(first); |
| 188 | + sigqueue_free_current(first); |
| 189 | } else { |
| 190 | /* |
| 191 | * Ok, it wasn't in the queue. This must be |
| 192 | @@ -560,6 +618,8 @@ int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info) |
| 193 | { |
| 194 | int signr; |
| 195 | |
| 196 | + WARN_ON_ONCE(tsk != current); |
| 197 | + |
| 198 | /* We only dequeue private signals from ourselves, we don't let |
| 199 | * signalfd steal them |
| 200 | */ |
| 201 | @@ -1485,7 +1545,8 @@ EXPORT_SYMBOL(kill_pid); |
| 202 | */ |
| 203 | struct sigqueue *sigqueue_alloc(void) |
| 204 | { |
| 205 | - struct sigqueue *q = __sigqueue_alloc(-1, current, GFP_KERNEL, 0); |
| 206 | + /* Preallocated sigqueue objects always from the slabcache ! */ |
| 207 | + struct sigqueue *q = __sigqueue_do_alloc(-1, current, GFP_KERNEL, 0, 1); |
| 208 | |
| 209 | if (q) |
| 210 | q->flags |= SIGQUEUE_PREALLOC; |
| 211 | -- |
| 212 | 2.9.3 |
| 213 | |