blob: 8675916597eebcbbf2fde82cef6284e37f8f6b44 [file] [log] [blame]
Allen Martin685e0f82016-07-26 19:34:29 -07001From 123a6303ad9225d288c91d1c8a216abc1749c7be Mon Sep 17 00:00:00 2001
2From: Thomas Gleixner <tglx@linutronix.de>
3Date: Fri, 3 Jul 2009 08:44:56 -0500
4Subject: [PATCH 048/317] signals: Allow rt tasks to cache one sigqueue struct
5X-NVConfidentiality: public
6
7To avoid allocation allow rt tasks to cache one sigqueue struct in
8task struct.
9
10Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
11Signed-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
20diff --git a/include/linux/sched.h b/include/linux/sched.h
21index 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 */
32diff --git a/include/linux/signal.h b/include/linux/signal.h
33index 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)
44diff --git a/kernel/exit.c b/kernel/exit.c
45index 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
57diff --git a/kernel/fork.c b/kernel/fork.c
58index 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;
69diff --git a/kernel/signal.c b/kernel/signal.c
70index 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--
2122.9.3
213