Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b3762bfc authored by Davi Arnaut's avatar Davi Arnaut Committed by Linus Torvalds
Browse files

signalfd: retrieve multiple signals with one read() call



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.

Signed-off-by: default avatarDavi E. M. Arnaut <davi@haxent.com.br>
Acked-by: default avatarDavide Libenzi <davidel@xmailserver.org>
Cc: Oleg Nesterov <oleg@tv-sign.ru>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 0532cb42
Loading
Loading
Loading
Loading
+75 −45
Original line number Original line Diff line number Diff line
@@ -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,64 +208,92 @@ 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,
 * Returns either the size of a "struct signalfd_siginfo", or zero if the
				int nonblock)
 * sighand we are attached to, has been orphaned. The "count" parameter
 * must be at least the size of a "struct signalfd_siginfo".
 */
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 ret;
	ssize_t res = 0;
	int locked, signo;
	siginfo_t info;
	struct signalfd_lockctx lk;
	struct signalfd_lockctx lk;
	DECLARE_WAITQUEUE(wait, current);
	DECLARE_WAITQUEUE(wait, current);


	if (count < sizeof(struct signalfd_siginfo))
	if (!signalfd_lock(ctx, &lk))
		return -EINVAL;
	locked = signalfd_lock(ctx, &lk);
	if (!locked)
		return 0;
		return 0;
	res = -EAGAIN;

	signo = dequeue_signal(lk.tsk, &ctx->sigmask, &info);
	ret = dequeue_signal(lk.tsk, &ctx->sigmask, info);
	if (signo == 0 && !(file->f_flags & O_NONBLOCK)) {
	switch (ret) {
	case 0:
		if (!nonblock)
			break;
		ret = -EAGAIN;
	default:
		signalfd_unlock(&lk);
		return ret;
	}

	add_wait_queue(&ctx->wqh, &wait);
	add_wait_queue(&ctx->wqh, &wait);
	for (;;) {
	for (;;) {
		set_current_state(TASK_INTERRUPTIBLE);
		set_current_state(TASK_INTERRUPTIBLE);
			signo = dequeue_signal(lk.tsk, &ctx->sigmask, &info);
		ret = dequeue_signal(lk.tsk, &ctx->sigmask, info);
			if (signo != 0)
		signalfd_unlock(&lk);
		if (ret != 0)
			break;
			break;
		if (signal_pending(current)) {
		if (signal_pending(current)) {
				res = -ERESTARTSYS;
			ret = -ERESTARTSYS;
			break;
			break;
		}
		}
			signalfd_unlock(&lk);
		schedule();
		schedule();
			locked = signalfd_lock(ctx, &lk);
		ret = signalfd_lock(ctx, &lk);
			if (unlikely(!locked)) {
		if (unlikely(!ret)) {
			/*
			/*
			 * Let the caller read zero byte, ala socket
			 * Let the caller read zero byte, ala socket
			 * recv() when the peer disconnect. This test
			 * recv() when the peer disconnect. This test
			 * must be done before doing a dequeue_signal(),
			 * must be done before doing a dequeue_signal(),
			 * because if the sighand has been orphaned,
			 * because if the sighand has been orphaned,
				 * the dequeue_signal() call is going to crash.
			 * the dequeue_signal() call is going to crash
			 * because ->sighand will be long gone.
			 */
			 */
				res = 0;
			 break;
			 break;
		}
		}
	}
	}

	remove_wait_queue(&ctx->wqh, &wait);
	remove_wait_queue(&ctx->wqh, &wait);
	__set_current_state(TASK_RUNNING);
	__set_current_state(TASK_RUNNING);

	return ret;
}
}
	if (likely(locked))
		signalfd_unlock(&lk);
	if (likely(signo))
		res = signalfd_copyinfo((struct signalfd_siginfo __user *) buf,
					&info);


	return res;
/*
 * Returns either the size of a "struct signalfd_siginfo", or zero if the
 * sighand we are attached to, has been orphaned. The "count" parameter
 * must be at least the size of a "struct signalfd_siginfo".
 */
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;
}
}


static const struct file_operations signalfd_fops = {
static const struct file_operations signalfd_fops = {