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