>From 64f8fc4b209c73114622b03a43ea196b01d777f8 Mon Sep 17 00:00:00 2001 From: Andrey Vagin Date: Wed, 26 Dec 2012 13:45:54 +0400 Subject: [PATCH] signalfd: add ability to get signal without removing from the queue (v2) pread(fd, buf, size, pos) with non-zero pos reads siginfo and leaves signal in a queue. pos = seq + SIGNALFD_*_OFFSET seq is a sequence number of a signal in a queue. SIGNALFD_PRIVATE_OFFSET - read from a private queue. SIGNALFD_SHARED_OFFSET - read from a shared queue. 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 Signed-off-by: Andrey Vagin --- fs/signalfd.c | 54 ++++++++++++++++++++++++++++++++++++++++++- include/uapi/linux/signalfd.h | 3 +++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/fs/signalfd.c b/fs/signalfd.c index ee60d5f..9e4e2fc 100644 --- a/fs/signalfd.c +++ b/fs/signalfd.c @@ -50,6 +50,50 @@ struct signalfd_ctx { sigset_t sigmask; }; +static ssize_t signalfd_peek(struct signalfd_ctx *ctx, + siginfo_t *info, unsigned long ppos) +{ + struct sigpending *pending; + struct sigqueue *q; + bool group = 0; + loff_t seq, i; + int ret = 0; + + if (ppos < SIGNALFD_PRIVATE_OFFSET) + return -EINVAL; + + if (ppos >= SIGNALFD_SHARED_OFFSET) { + seq = ppos - SIGNALFD_SHARED_OFFSET; + group = 1; + } else + seq = ppos - SIGNALFD_PRIVATE_OFFSET; + + spin_lock_irq(¤t->sighand->siglock); + + if (group) + pending = ¤t->signal->shared_pending; + else + pending = ¤t->pending; + + i = 0; + list_for_each_entry(q, &pending->list, list) { + if (sigismember(&ctx->sigmask, q->info.si_signo)) + continue; + + if (i == seq) { + copy_siginfo(info, &q->info); + ret = info->si_signo; + break; + } + + i++; + } + + spin_unlock_irq(¤t->sighand->siglock); + + return ret; +} + static int signalfd_release(struct inode *inode, struct file *file) { kfree(file->private_data); @@ -230,7 +274,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; @@ -242,6 +290,9 @@ static ssize_t signalfd_read(struct file *file, char __user *buf, size_t count, if (ret < 0) break; + if (*ppos) + (*ppos)++; + siginfo++; total += ret; nonblock = 1; @@ -321,6 +372,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 { diff --git a/include/uapi/linux/signalfd.h b/include/uapi/linux/signalfd.h index bc31849..c6eb535 100644 --- a/include/uapi/linux/signalfd.h +++ b/include/uapi/linux/signalfd.h @@ -17,6 +17,9 @@ #define SFD_NONBLOCK O_NONBLOCK #define SFD_RAW O_DIRECT +#define SIGNALFD_SHARED_OFFSET (1LL << 62) +#define SIGNALFD_PRIVATE_OFFSET 1 + struct signalfd_siginfo { __u32 ssi_signo; __s32 ssi_errno; -- 1.7.11.7