|
@@ -11,6 +11,8 @@
|
|
* Now using anonymous inode source.
|
|
* Now using anonymous inode source.
|
|
* Thanks to Oleg Nesterov for useful code review and suggestions.
|
|
* Thanks to Oleg Nesterov for useful code review and suggestions.
|
|
* More comments and suggestions from Arnd Bergmann.
|
|
* 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>
|
|
#include <linux/file.h>
|
|
@@ -206,6 +208,59 @@ static int signalfd_copyinfo(struct signalfd_siginfo __user *uinfo,
|
|
return err ? -EFAULT: sizeof(*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
|
|
* Returns either the size of a "struct signalfd_siginfo", or zero if the
|
|
* sighand we are attached to, has been orphaned. The "count" parameter
|
|
* 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,
|
|
loff_t *ppos)
|
|
loff_t *ppos)
|
|
{
|
|
{
|
|
struct signalfd_ctx *ctx = file->private_data;
|
|
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;
|
|
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;
|
|
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 = {
|
|
static const struct file_operations signalfd_fops = {
|