aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavi Arnaut <davi@haxent.com.br>2007-05-23 16:58:04 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-05-23 23:14:14 -0400
commitb3762bfc8d046342db664d855f8f875e8a4c2ca1 (patch)
treefefa7bf1aecbdf458f31d4896498282f5ee9d1e7
parent0532cb427ed2a5957d39e98cb5a4741b68fc6102 (diff)
signalfd: retrieve multiple signals with one read() call
Gathering signals in bulk enables server applications to drain a signal queue (almost full of realtime signals) more efficiently by reducing the syscall and file look-up overhead. Very similar to the sigtimedwait4() call described by Niels Provos, Chuck Lever, and Stephen Tweedie in a paper entitled "Analyzing the Overload Behavior of a Simple Web Server". The paper lists more details and advantages. Signed-off-by: Davi E. M. Arnaut <davi@haxent.com.br> Acked-by: Davide Libenzi <davidel@xmailserver.org> Cc: Oleg Nesterov <oleg@tv-sign.ru> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--fs/signalfd.c120
1 files changed, 75 insertions, 45 deletions
diff --git a/fs/signalfd.c b/fs/signalfd.c
index 7cfeab412b45..f1da89203a9a 100644
--- a/fs/signalfd.c
+++ b/fs/signalfd.c
@@ -11,6 +11,8 @@
11 * Now using anonymous inode source. 11 * Now using anonymous inode source.
12 * Thanks to Oleg Nesterov for useful code review and suggestions. 12 * Thanks to Oleg Nesterov for useful code review and suggestions.
13 * More comments and suggestions from Arnd Bergmann. 13 * More comments and suggestions from Arnd Bergmann.
14 * Sat May 19, 2007: Davi E. M. Arnaut <davi@haxent.com.br>
15 * Retrieve multiple signals with one read() call
14 */ 16 */
15 17
16#include <linux/file.h> 18#include <linux/file.h>
@@ -206,6 +208,59 @@ static int signalfd_copyinfo(struct signalfd_siginfo __user *uinfo,
206 return err ? -EFAULT: sizeof(*uinfo); 208 return err ? -EFAULT: sizeof(*uinfo);
207} 209}
208 210
211static ssize_t signalfd_dequeue(struct signalfd_ctx *ctx, siginfo_t *info,
212 int nonblock)
213{
214 ssize_t ret;
215 struct signalfd_lockctx lk;
216 DECLARE_WAITQUEUE(wait, current);
217
218 if (!signalfd_lock(ctx, &lk))
219 return 0;
220
221 ret = dequeue_signal(lk.tsk, &ctx->sigmask, info);
222 switch (ret) {
223 case 0:
224 if (!nonblock)
225 break;
226 ret = -EAGAIN;
227 default:
228 signalfd_unlock(&lk);
229 return ret;
230 }
231
232 add_wait_queue(&ctx->wqh, &wait);
233 for (;;) {
234 set_current_state(TASK_INTERRUPTIBLE);
235 ret = dequeue_signal(lk.tsk, &ctx->sigmask, info);
236 signalfd_unlock(&lk);
237 if (ret != 0)
238 break;
239 if (signal_pending(current)) {
240 ret = -ERESTARTSYS;
241 break;
242 }
243 schedule();
244 ret = signalfd_lock(ctx, &lk);
245 if (unlikely(!ret)) {
246 /*
247 * Let the caller read zero byte, ala socket
248 * recv() when the peer disconnect. This test
249 * must be done before doing a dequeue_signal(),
250 * because if the sighand has been orphaned,
251 * the dequeue_signal() call is going to crash
252 * because ->sighand will be long gone.
253 */
254 break;
255 }
256 }
257
258 remove_wait_queue(&ctx->wqh, &wait);
259 __set_current_state(TASK_RUNNING);
260
261 return ret;
262}
263
209/* 264/*
210 * Returns either the size of a "struct signalfd_siginfo", or zero if the 265 * Returns either the size of a "struct signalfd_siginfo", or zero if the
211 * sighand we are attached to, has been orphaned. The "count" parameter 266 * sighand we are attached to, has been orphaned. The "count" parameter
@@ -215,55 +270,30 @@ static ssize_t signalfd_read(struct file *file, char __user *buf, size_t count,
215 loff_t *ppos) 270 loff_t *ppos)
216{ 271{
217 struct signalfd_ctx *ctx = file->private_data; 272 struct signalfd_ctx *ctx = file->private_data;
218 ssize_t res = 0; 273 struct signalfd_siginfo __user *siginfo;
219 int locked, signo; 274 int nonblock = file->f_flags & O_NONBLOCK;
275 ssize_t ret, total = 0;
220 siginfo_t info; 276 siginfo_t info;
221 struct signalfd_lockctx lk;
222 DECLARE_WAITQUEUE(wait, current);
223 277
224 if (count < sizeof(struct signalfd_siginfo)) 278 count /= sizeof(struct signalfd_siginfo);
279 if (!count)
225 return -EINVAL; 280 return -EINVAL;
226 locked = signalfd_lock(ctx, &lk);
227 if (!locked)
228 return 0;
229 res = -EAGAIN;
230 signo = dequeue_signal(lk.tsk, &ctx->sigmask, &info);
231 if (signo == 0 && !(file->f_flags & O_NONBLOCK)) {
232 add_wait_queue(&ctx->wqh, &wait);
233 for (;;) {
234 set_current_state(TASK_INTERRUPTIBLE);
235 signo = dequeue_signal(lk.tsk, &ctx->sigmask, &info);
236 if (signo != 0)
237 break;
238 if (signal_pending(current)) {
239 res = -ERESTARTSYS;
240 break;
241 }
242 signalfd_unlock(&lk);
243 schedule();
244 locked = signalfd_lock(ctx, &lk);
245 if (unlikely(!locked)) {
246 /*
247 * Let the caller read zero byte, ala socket
248 * recv() when the peer disconnect. This test
249 * must be done before doing a dequeue_signal(),
250 * because if the sighand has been orphaned,
251 * the dequeue_signal() call is going to crash.
252 */
253 res = 0;
254 break;
255 }
256 }
257 remove_wait_queue(&ctx->wqh, &wait);
258 __set_current_state(TASK_RUNNING);
259 }
260 if (likely(locked))
261 signalfd_unlock(&lk);
262 if (likely(signo))
263 res = signalfd_copyinfo((struct signalfd_siginfo __user *) buf,
264 &info);
265 281
266 return res; 282 siginfo = (struct signalfd_siginfo __user *) buf;
283
284 do {
285 ret = signalfd_dequeue(ctx, &info, nonblock);
286 if (unlikely(ret <= 0))
287 break;
288 ret = signalfd_copyinfo(siginfo, &info);
289 if (ret < 0)
290 break;
291 siginfo++;
292 total += ret;
293 nonblock = 1;
294 } while (--count);
295
296 return total ? total : ret;
267} 297}
268 298
269static const struct file_operations signalfd_fops = { 299static const struct file_operations signalfd_fops = {