signalfd: retrieve multiple signals with one read() call
PATCH 1/1 signalfd: retrieve multiple signals with one read() call
Hi,
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.
fs/signalfd.c +80 -45
@@ -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 <[email protected]> +* Retrieve multiple signals with one read() call */ #include <linux/file.h>
@@ -206,6 +208,64 @@ 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) +{ +int locked; +ssize_t ret; +struct signalfd_lockctx lk; +DECLARE_WAITQUEUE (wait, current); + + locked =signalfd_lock (ctx, &lk); +if (!locked) +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); +if (ret !=0 ) +break ; +if (signal_pending (current)) { + ret = -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. +*/ + ret =0 ; +break ; + } + } + +remove_wait_queue (&ctx->wqh, &wait); +__set_current_state (TASK_RUNNING ); + +if (likely (locked)) +signalfd_unlock (&lk); + +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
@@ -215,55 +275,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 = {
Quoted 10 lines · Davi Arnaut
Hi,
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.
That paper is a pre-epoll thing. Nowadays I see no value in using RT signals as main event gathering mechanism ;)
Quoted 1 line
Signed-off-by Davi E. M. Arnaut <[email protected]>
The patch looks otherwise good to me, and I see no reasons why we shoulnd't be fetching more than one signal if available.
- Davide
Quoted 11 lines · On Sat, 19 May 2007 21:07:11 -0300 Davi Arnaut <[email protected]> wrote:
Hi,
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.
static ssize_t signalfd_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
struct signalfd_ctx *ctx = file->private_data;
struct signalfd_siginfo __user *siginfo;
int nonblock = file->f_flags & O_NONBLOCK;
ssize_t ret, total = 0;
siginfo_t info;
count /= sizeof(struct signalfd_siginfo);
if (!count)
return -EINVAL;
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;
}If 'count' is not a multiple of sizeof(struct signalfd_siginfo)), the read() will return the next smallest multiple of `count'.
That is, unless `count' happens to be less than 1*sizeof(struct signalfd_siginfo)), in which case we return -EINVAL.
This seems inconsistent.
Also, I'm desperately hunting for the place where we zero out that local siginfo_t, and I ain't finding it. Someone please convince me that we're not leaking bits of kernel memory out to userspace in that thing.
Quoted 7 lines · Andrew Morton
If 'count' is not a multiple of
sizeof(struct signalfd_siginfo)), theread()will return the next smallest multiple of `count'.That is, unless `count' happens to be less than 1*sizeof(struct
signalfd_siginfo)), in which case we return-EINVAL.This seems inconsistent.
I think it fits the rule "buffer must be big enough for at least one sigingo". We use the special return 0; as indicator that the process we were monitoring signals, detached the sighand.
Quoted 3 lines
Also, I'm desperately hunting for the place where we zero out that local
siginfo_t, and I ain't finding it. Someone please convince me that we're not leaking bits of kernel memory out to userspace in that thing.
Hmm, __clear_user()?
- Davide
Quoted 1 line · Davide Libenzi
I think it fits the rule "buffer must be big enough for at least one sigingo".
^^^^^^^
/me wonders what sizeof(sigingo) is :)
- Davide
Quoted 5 lines · On Sun, 20 May 2007 21:14:38 -0700 (PDT) Davide Libenzi…
Quoted 3 lines · Andrew Morton
If 'count' is not a multiple of
sizeof(struct signalfd_siginfo)), theread()will return the next smallest multiple of `count'.
Quoted 9 lines
Quoted 4 lines
That is, unless `count' happens to be less than 1*sizeof(struct
signalfd_siginfo)), in which case we return-EINVAL.This seems inconsistent.
I think it fits the rule "buffer must be big enough for at least one sigingo". We use the special return 0; as indicator that the process we were monitoring signals, detached the sighand.
hm. Kernel violates proper read() semantics in many places. Looks like we just did it again.
Quoted 6 lines
Quoted 4 lines
Also, I'm desperately hunting for the place where we zero out that local
siginfo_t, and I ain't finding it. Someone please convince me that we're not leaking bits of kernel memory out to userspace in that thing.Hmm,
__clear_user()?
oic, yes, that thing. Usually we'd zero out the on-stack struct, assemble it then copy out the whole thing. I guess doing it the way you have saves a few instructions. But it's the cache hit against *uinfo which will have most of the cost, and we can't do anything about that.
Unless we just remove the __clear_user() altogether. Who said that "Unused memebers should be zero"?
Quoted 7 lines · Andrew Morton
Quoted 4 lines
I think it fits the rule "buffer must be big enough for at least one sigingo". We use the special return 0; as indicator that the process we were monitoring signals, detached the sighand.
hm. Kernel violates proper
read()semantics in many places. Looks like we just did it again.
I think we can have the check that "if size == 0 return 0". The above cited return-0-on-detch would still apply for enough sized buffers. So:
1) size == 0, return 0 (POSIX wants this)
2) size < sizeof(signalfd_siginfo), return EINVAL
3) size >= sizeof(signalfd_siginfo) && DETACH, return 0
The signalfd falls into what POSIX defined as "special file", and can return a lower-than-size result.
Quoted 2 lines
Unless we just remove the
__clear_user()altogether. Who said that "Unused memebers should be zero"?
Because it is a typically used value for still-unused/reserved members? Better than random values I think ;)
Members validity is driven by si_code & SI_MASK anyway.
- Davide
Quoted 13 lines · On Sun, 20 May 2007 22:05:00 -0700 (PDT) Davide Libenzi…
Quoted 7 lines · Andrew Morton
Quoted 4 lines
I think it fits the rule "buffer must be big enough for at least one sigingo". We use the special return 0; as indicator that the process we were monitoring signals, detached the sighand.
hm. Kernel violates proper
read()semantics in many places. Looks like we just did it again.I think we can have the check that "if size == 0 return 0". The above cited return-0-on-detch would still apply for enough sized buffers. So:
Quoted 9 lines
1) size == 0, return 0 (POSIX wants this)
2) size <
sizeof(signalfd_siginfo), return EINVAL3) size >=
sizeof(signalfd_siginfo)&& DETACH, return 0The signalfd falls into what POSIX defined as "special file", and can return a lower-than-size result.
hm, well. I'd suggest that we do what makes most sense, rather than warping things to try to obey the letter of posix.
Quoted 7 lines
Quoted 3 lines
Unless we just remove the
__clear_user()altogether. Who said that "Unused memebers should be zero"?Because it is a typically used value for still-unused/reserved members? Better than random values I think ;)
Members validity is driven bysi_code&SI_MASKanyway.
Sure. And it'd be a bit rude to return 128 from the read() but to only have written to a few bytes of the user's memory.
otoh, only-writing-a-few-bytes will be usefully quicker than zapping the whole 128b, particularly on small-cacheline CPUs.