Skip to content

Commit

Permalink
signalfd: retrieve multiple signals with one read() call
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Davi Arnaut authored and Linus Torvalds committed May 24, 2007
1 parent 0532cb4 commit b3762bf
Showing 1 changed file with 75 additions and 45 deletions.
120 changes: 75 additions & 45 deletions fs/signalfd.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
* Now using anonymous inode source.
* Thanks to Oleg Nesterov for useful code review and suggestions.
* More comments and suggestions from Arnd Bergmann.
* Sat May 19, 2007: Davi E. M. Arnaut <davi@haxent.com.br>
* Retrieve multiple signals with one read() call
*/

#include <linux/file.h>
Expand Down Expand Up @@ -206,6 +208,59 @@ static int signalfd_copyinfo(struct signalfd_siginfo __user *uinfo,
return err ? -EFAULT: sizeof(*uinfo);
}

static ssize_t signalfd_dequeue(struct signalfd_ctx *ctx, siginfo_t *info,
int nonblock)
{
ssize_t ret;
struct signalfd_lockctx lk;
DECLARE_WAITQUEUE(wait, current);

if (!signalfd_lock(ctx, &lk))
return 0;

ret = dequeue_signal(lk.tsk, &ctx->sigmask, info);
switch (ret) {
case 0:
if (!nonblock)
break;
ret = -EAGAIN;
default:
signalfd_unlock(&lk);
return ret;
}

add_wait_queue(&ctx->wqh, &wait);
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
ret = dequeue_signal(lk.tsk, &ctx->sigmask, info);
signalfd_unlock(&lk);
if (ret != 0)
break;
if (signal_pending(current)) {
ret = -ERESTARTSYS;
break;
}
schedule();
ret = signalfd_lock(ctx, &lk);
if (unlikely(!ret)) {
/*
* Let the caller read zero byte, ala socket
* recv() when the peer disconnect. This test
* must be done before doing a dequeue_signal(),
* because if the sighand has been orphaned,
* the dequeue_signal() call is going to crash
* because ->sighand will be long gone.
*/
break;
}
}

remove_wait_queue(&ctx->wqh, &wait);
__set_current_state(TASK_RUNNING);

return ret;
}

/*
* Returns either the size of a "struct signalfd_siginfo", or zero if the
* sighand we are attached to, has been orphaned. The "count" parameter
Expand All @@ -215,55 +270,30 @@ static ssize_t signalfd_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
struct signalfd_ctx *ctx = file->private_data;
ssize_t res = 0;
int locked, signo;
struct signalfd_siginfo __user *siginfo;
int nonblock = file->f_flags & O_NONBLOCK;
ssize_t ret, total = 0;
siginfo_t info;
struct signalfd_lockctx lk;
DECLARE_WAITQUEUE(wait, current);

if (count < sizeof(struct signalfd_siginfo))
count /= sizeof(struct signalfd_siginfo);
if (!count)
return -EINVAL;
locked = signalfd_lock(ctx, &lk);
if (!locked)
return 0;
res = -EAGAIN;
signo = dequeue_signal(lk.tsk, &ctx->sigmask, &info);
if (signo == 0 && !(file->f_flags & O_NONBLOCK)) {
add_wait_queue(&ctx->wqh, &wait);
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
signo = dequeue_signal(lk.tsk, &ctx->sigmask, &info);
if (signo != 0)
break;
if (signal_pending(current)) {
res = -ERESTARTSYS;
break;
}
signalfd_unlock(&lk);
schedule();
locked = signalfd_lock(ctx, &lk);
if (unlikely(!locked)) {
/*
* Let the caller read zero byte, ala socket
* recv() when the peer disconnect. This test
* must be done before doing a dequeue_signal(),
* because if the sighand has been orphaned,
* the dequeue_signal() call is going to crash.
*/
res = 0;
break;
}
}
remove_wait_queue(&ctx->wqh, &wait);
__set_current_state(TASK_RUNNING);
}
if (likely(locked))
signalfd_unlock(&lk);
if (likely(signo))
res = signalfd_copyinfo((struct signalfd_siginfo __user *) buf,
&info);

return res;
siginfo = (struct signalfd_siginfo __user *) buf;

do {
ret = signalfd_dequeue(ctx, &info, nonblock);
if (unlikely(ret <= 0))
break;
ret = signalfd_copyinfo(siginfo, &info);
if (ret < 0)
break;
siginfo++;
total += ret;
nonblock = 1;
} while (--count);

return total ? total : ret;
}

static const struct file_operations signalfd_fops = {
Expand Down

0 comments on commit b3762bf

Please sign in to comment.