summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
authorAndrey Vagin <avagin@openvz.org>2013-01-24 13:14:29 +1100
committerStephen Rothwell <sfr@canb.auug.org.au>2013-02-04 18:00:55 +1100
commitf0607bbee88d001d72b4ed576c7e3ab50f7b5f3e (patch)
treec6869868145b690ee642120e59db93e5f01357d3 /fs
parentb7db8ae5ad2344f8a5d9bf5153cad36e88e92e64 (diff)
signalfd: add ability to read siginfos without dequeuing signals
pread(fd, buf, size, pos) with non-zero pos returns siginfos without dequeuing signals. A sequence number and a queue are encoded in pos. pos = seq + SFD_*_OFFSET seq is a sequence number of a signal in a queue. SFD_PER_THREAD_QUEUE_OFFSET - read signals from a per-thread queue. SFD_SHARED_QUEUE_OFFSET - read signals from a shared (process wide) queue. This functionality is required for checkpointing pending signals. v2: llseek() can't be used here, because peek_offset/f_pos/whatever has to be shared with all processes which have this file opened. Suppose that the task forks after sys_signalfd(). Now if parent or child do llseek this affects them both. This is insane because signalfd is "strange" to say at least, fork/dup/etc inherits signalfd_ctx but not the" source" of the data. // Oleg Nesterov v3,v4: minor cleanups Signed-off-by: Andrey Vagin <avagin@openvz.org> Cc: Alexander Viro <viro@zeniv.linux.org.uk> Cc: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com> Cc: David Howells <dhowells@redhat.com> Cc: Dave Jones <davej@redhat.com> Cc: Andrey Vagin <avagin@openvz.org> Cc: Michael Kerrisk <mtk.manpages@gmail.com> Cc: Pavel Emelyanov <xemul@parallels.com> Cc: Cyrill Gorcunov <gorcunov@openvz.org> Cc: Michael Kerrisk <mtk.manpages@gmail.com> Reviewed-by: Oleg Nesterov <oleg@redhat.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/signalfd.c45
1 files changed, 44 insertions, 1 deletions
diff --git a/fs/signalfd.c b/fs/signalfd.c
index c723b5d56c8f..229e480aac28 100644
--- a/fs/signalfd.c
+++ b/fs/signalfd.c
@@ -51,6 +51,44 @@ struct signalfd_ctx {
sigset_t sigmask;
};
+static ssize_t signalfd_peek(struct signalfd_ctx *ctx,
+ siginfo_t *info, loff_t *ppos)
+{
+ struct sigpending *pending;
+ struct sigqueue *q;
+ loff_t seq;
+ int ret = 0;
+
+ if (*ppos >= SFD_SHARED_QUEUE_OFFSET) {
+ pending = &current->signal->shared_pending;
+ seq = *ppos - SFD_SHARED_QUEUE_OFFSET;
+ } else if (*ppos >= SFD_PER_THREAD_QUEUE_OFFSET) {
+ pending = &current->pending;
+ seq = *ppos - SFD_PER_THREAD_QUEUE_OFFSET;
+ } else
+ return -EINVAL;
+
+ spin_lock_irq(&current->sighand->siglock);
+
+ list_for_each_entry(q, &pending->list, list) {
+ if (sigismember(&ctx->sigmask, q->info.si_signo))
+ continue;
+
+ if (seq-- == 0) {
+ copy_siginfo(info, &q->info);
+ ret = info->si_signo;
+ break;
+ }
+ }
+
+ spin_unlock_irq(&current->sighand->siglock);
+
+ if (ret)
+ (*ppos)++;
+
+ return ret;
+}
+
static int signalfd_release(struct inode *inode, struct file *file)
{
kfree(file->private_data);
@@ -249,7 +287,11 @@ static ssize_t signalfd_read(struct file *file, char __user *buf, size_t count,
siginfo = (struct signalfd_siginfo __user *) buf;
do {
- ret = signalfd_dequeue(ctx, &info, nonblock);
+ if (*ppos == 0)
+ ret = signalfd_dequeue(ctx, &info, nonblock);
+ else
+ ret = signalfd_peek(ctx, &info, ppos);
+
if (unlikely(ret <= 0))
break;
@@ -339,6 +381,7 @@ SYSCALL_DEFINE4(signalfd4, int, ufd, sigset_t __user *, user_mask,
}
file->f_flags |= flags & SFD_RAW;
+ file->f_mode |= FMODE_PREAD;
fd_install(ufd, file);
} else {