[CRIU] [PATCH 2/3] signalfd: add ability to get signal without removing from the queue

Andrey Vagin avagin at openvz.org
Thu Nov 29 11:51:50 EST 2012


signalfd should be called with the flag SFD_PEEK.

The operation read() returns siginfo, but the signal remains in a queue.
signalfd has an internal offset, which is incremented on each siginfo
and the next siginfo is choosen according with this offset in the queue.

If a signalfd is created with the flag SFD_GROUP, signals will be
received from a group signal queue.

If a signalfd is created with SFD_PEEK, read() returns siginfo instead
of signalfd_siginfo, because signalfd_siginfo is not full.
For example if a signal has been sent by rt_sigqueueinfo,
signalfd_siginfo will not contain full data from siginfo.

si_code is not restricted, it contains SI_CODE, otherwise we can't
restore siginfo.

This fuctionality will be used for checkpointing pending signals.

Signed-off-by: Andrey Vagin <avagin at openvz.org>
---
 fs/signalfd.c                 | 51 +++++++++++++++++++++++++++++++++++++------
 include/uapi/linux/signalfd.h |  2 ++
 2 files changed, 46 insertions(+), 7 deletions(-)

diff --git a/fs/signalfd.c b/fs/signalfd.c
index b534869..bd10b06 100644
--- a/fs/signalfd.c
+++ b/fs/signalfd.c
@@ -48,6 +48,7 @@ void signalfd_cleanup(struct sighand_struct *sighand)
 
 struct signalfd_ctx {
 	sigset_t sigmask;
+	unsigned int peek_offset;
 };
 
 static int signalfd_release(struct inode *inode, struct file *file)
@@ -154,6 +155,17 @@ static int signalfd_copyinfo(struct signalfd_siginfo __user *uinfo,
 	return err ? -EFAULT: sizeof(*uinfo);
 }
 
+static ssize_t signalfd_peek(struct signalfd_ctx *ctx, siginfo_t *info, int group)
+{
+	int ret;
+
+	spin_lock_irq(&current->sighand->siglock);
+	ret = peek_signal(current, &ctx->sigmask, info, ctx->peek_offset, group);
+	spin_unlock_irq(&current->sighand->siglock);
+
+	return ret;
+}
+
 static ssize_t signalfd_dequeue(struct signalfd_ctx *ctx, siginfo_t *info,
 				int nonblock)
 {
@@ -205,21 +217,40 @@ static ssize_t signalfd_read(struct file *file, char __user *buf, size_t count,
 	struct signalfd_ctx *ctx = file->private_data;
 	struct signalfd_siginfo __user *siginfo;
 	int nonblock = file->f_flags & O_NONBLOCK;
+	bool group = file->f_flags & SFD_GROUP;
+	bool peek = file->f_flags & SFD_PEEK;
 	ssize_t ret, total = 0;
 	siginfo_t info;
 
+	BUILD_BUG_ON(sizeof(siginfo_t) != sizeof(struct signalfd_siginfo));
+
 	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;
+		if (peek) {
+			siginfo_t *uinfo = (siginfo_t *) siginfo;
+			ret = signalfd_peek(ctx, &info, group);
+			if (unlikely(ret <= 0))
+				break;
+
+			ret = copy_siginfo_to_user(uinfo, &info);
+			ret |= __put_user(info.si_code, &uinfo->si_code);
+			if (ret == 0) {
+				ctx->peek_offset++;
+				ret = sizeof(struct signalfd_siginfo);
+			}
+		} else {
+			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;
@@ -262,7 +293,7 @@ SYSCALL_DEFINE4(signalfd4, int, ufd, sigset_t __user *, user_mask,
 	BUILD_BUG_ON(SFD_CLOEXEC != O_CLOEXEC);
 	BUILD_BUG_ON(SFD_NONBLOCK != O_NONBLOCK);
 
-	if (flags & ~(SFD_CLOEXEC | SFD_NONBLOCK))
+	if (flags & ~(SFD_CLOEXEC | SFD_NONBLOCK | SFD_PEEK | SFD_GROUP))
 		return -EINVAL;
 
 	if (sizemask != sizeof(sigset_t) ||
@@ -276,6 +307,7 @@ SYSCALL_DEFINE4(signalfd4, int, ufd, sigset_t __user *, user_mask,
 		if (!ctx)
 			return -ENOMEM;
 
+		ctx->peek_offset = 0;
 		ctx->sigmask = sigmask;
 
 		/*
@@ -286,6 +318,11 @@ SYSCALL_DEFINE4(signalfd4, int, ufd, sigset_t __user *, user_mask,
 				       O_RDWR | (flags & (O_CLOEXEC | O_NONBLOCK)));
 		if (ufd < 0)
 			kfree(ctx);
+		else {
+			struct fd f = fdget(ufd);
+			f.file->f_flags |= (flags & (SFD_GROUP | SFD_PEEK));
+			fdput(f);
+		}
 	} else {
 		struct fd f = fdget(ufd);
 		if (!f.file)
diff --git a/include/uapi/linux/signalfd.h b/include/uapi/linux/signalfd.h
index 492c6de..f377a39 100644
--- a/include/uapi/linux/signalfd.h
+++ b/include/uapi/linux/signalfd.h
@@ -15,6 +15,8 @@
 /* Flags for signalfd4.  */
 #define SFD_CLOEXEC O_CLOEXEC
 #define SFD_NONBLOCK O_NONBLOCK
+#define SFD_PEEK O_DIRECT
+#define SFD_GROUP O_DIRECTORY
 
 struct signalfd_siginfo {
 	__u32 ssi_signo;
-- 
1.7.11.7



More information about the CRIU mailing list