Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 55926
b: refs/heads/master
c: fba2afa
h: refs/heads/master
v: v3
  • Loading branch information
Davide Libenzi authored and Linus Torvalds committed May 11, 2007
1 parent 7330bbb commit 91e4b4a
Show file tree
Hide file tree
Showing 15 changed files with 509 additions and 8 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 5dc8bf8132d59c03fe2562bce165c2f03f021687
refs/heads/master: fba2afaaec790dc5ab4ae8827972f342211bbb86
1 change: 1 addition & 0 deletions trunk/fs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ obj-$(CONFIG_INOTIFY) += inotify.o
obj-$(CONFIG_INOTIFY_USER) += inotify_user.o
obj-$(CONFIG_EPOLL) += eventpoll.o
obj-$(CONFIG_ANON_INODES) += anon_inodes.o
obj-$(CONFIG_SIGNALFD) += signalfd.o
obj-$(CONFIG_COMPAT) += compat.o compat_ioctl.o

nfsd-$(CONFIG_NFSD) := nfsctl.o
Expand Down
11 changes: 9 additions & 2 deletions trunk/fs/exec.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
#include <linux/tsacct_kern.h>
#include <linux/cn_proc.h>
#include <linux/audit.h>
#include <linux/signalfd.h>

#include <asm/uaccess.h>
#include <asm/mmu_context.h>
Expand Down Expand Up @@ -581,6 +582,13 @@ static int de_thread(struct task_struct *tsk)
struct task_struct *leader = NULL;
int count;

/*
* Tell all the sighand listeners that this sighand has
* been detached. The signalfd_detach() function grabs the
* sighand lock, if signal listeners are present on the sighand.
*/
signalfd_detach(tsk);

/*
* If we don't share sighandlers, then we aren't sharing anything
* and we can just re-use it all.
Expand Down Expand Up @@ -757,8 +765,7 @@ static int de_thread(struct task_struct *tsk)
spin_unlock(&oldsighand->siglock);
write_unlock_irq(&tasklist_lock);

if (atomic_dec_and_test(&oldsighand->count))
kmem_cache_free(sighand_cachep, oldsighand);
__cleanup_sighand(oldsighand);
}

BUG_ON(!thread_group_leader(tsk));
Expand Down
349 changes: 349 additions & 0 deletions trunk/fs/signalfd.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,349 @@
/*
* fs/signalfd.c
*
* Copyright (C) 2003 Linus Torvalds
*
* Mon Mar 5, 2007: Davide Libenzi <davidel@xmailserver.org>
* Changed ->read() to return a siginfo strcture instead of signal number.
* Fixed locking in ->poll().
* Added sighand-detach notification.
* Added fd re-use in sys_signalfd() syscall.
* Now using anonymous inode source.
* Thanks to Oleg Nesterov for useful code review and suggestions.
* More comments and suggestions from Arnd Bergmann.
*/

#include <linux/file.h>
#include <linux/poll.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/list.h>
#include <linux/anon_inodes.h>
#include <linux/signalfd.h>

struct signalfd_ctx {
struct list_head lnk;
wait_queue_head_t wqh;
sigset_t sigmask;
struct task_struct *tsk;
};

struct signalfd_lockctx {
struct task_struct *tsk;
unsigned long flags;
};

/*
* Tries to acquire the sighand lock. We do not increment the sighand
* use count, and we do not even pin the task struct, so we need to
* do it inside an RCU read lock, and we must be prepared for the
* ctx->tsk going to NULL (in signalfd_deliver()), and for the sighand
* being detached. We return 0 if the sighand has been detached, or
* 1 if we were able to pin the sighand lock.
*/
static int signalfd_lock(struct signalfd_ctx *ctx, struct signalfd_lockctx *lk)
{
struct sighand_struct *sighand = NULL;

rcu_read_lock();
lk->tsk = rcu_dereference(ctx->tsk);
if (likely(lk->tsk != NULL))
sighand = lock_task_sighand(lk->tsk, &lk->flags);
rcu_read_unlock();

if (sighand && !ctx->tsk) {
unlock_task_sighand(lk->tsk, &lk->flags);
sighand = NULL;
}

return sighand != NULL;
}

static void signalfd_unlock(struct signalfd_lockctx *lk)
{
unlock_task_sighand(lk->tsk, &lk->flags);
}

/*
* This must be called with the sighand lock held.
*/
void signalfd_deliver(struct task_struct *tsk, int sig)
{
struct sighand_struct *sighand = tsk->sighand;
struct signalfd_ctx *ctx, *tmp;

BUG_ON(!sig);
list_for_each_entry_safe(ctx, tmp, &sighand->signalfd_list, lnk) {
/*
* We use a negative signal value as a way to broadcast that the
* sighand has been orphaned, so that we can notify all the
* listeners about this. Remember the ctx->sigmask is inverted,
* so if the user is interested in a signal, that corresponding
* bit will be zero.
*/
if (sig < 0) {
if (ctx->tsk == tsk) {
ctx->tsk = NULL;
list_del_init(&ctx->lnk);
wake_up(&ctx->wqh);
}
} else {
if (!sigismember(&ctx->sigmask, sig))
wake_up(&ctx->wqh);
}
}
}

static void signalfd_cleanup(struct signalfd_ctx *ctx)
{
struct signalfd_lockctx lk;

/*
* This is tricky. If the sighand is gone, we do not need to remove
* context from the list, the list itself won't be there anymore.
*/
if (signalfd_lock(ctx, &lk)) {
list_del(&ctx->lnk);
signalfd_unlock(&lk);
}
kfree(ctx);
}

static int signalfd_release(struct inode *inode, struct file *file)
{
signalfd_cleanup(file->private_data);
return 0;
}

static unsigned int signalfd_poll(struct file *file, poll_table *wait)
{
struct signalfd_ctx *ctx = file->private_data;
unsigned int events = 0;
struct signalfd_lockctx lk;

poll_wait(file, &ctx->wqh, wait);

/*
* Let the caller get a POLLIN in this case, ala socket recv() when
* the peer disconnects.
*/
if (signalfd_lock(ctx, &lk)) {
if (next_signal(&lk.tsk->pending, &ctx->sigmask) > 0 ||
next_signal(&lk.tsk->signal->shared_pending,
&ctx->sigmask) > 0)
events |= POLLIN;
signalfd_unlock(&lk);
} else
events |= POLLIN;

return events;
}

/*
* Copied from copy_siginfo_to_user() in kernel/signal.c
*/
static int signalfd_copyinfo(struct signalfd_siginfo __user *uinfo,
siginfo_t const *kinfo)
{
long err;

BUILD_BUG_ON(sizeof(struct signalfd_siginfo) != 128);

/*
* Unused memebers should be zero ...
*/
err = __clear_user(uinfo, sizeof(*uinfo));

/*
* If you change siginfo_t structure, please be sure
* this code is fixed accordingly.
*/
err |= __put_user(kinfo->si_signo, &uinfo->signo);
err |= __put_user(kinfo->si_errno, &uinfo->err);
err |= __put_user((short)kinfo->si_code, &uinfo->code);
switch (kinfo->si_code & __SI_MASK) {
case __SI_KILL:
err |= __put_user(kinfo->si_pid, &uinfo->pid);
err |= __put_user(kinfo->si_uid, &uinfo->uid);
break;
case __SI_TIMER:
err |= __put_user(kinfo->si_tid, &uinfo->tid);
err |= __put_user(kinfo->si_overrun, &uinfo->overrun);
err |= __put_user((long)kinfo->si_ptr, &uinfo->svptr);
break;
case __SI_POLL:
err |= __put_user(kinfo->si_band, &uinfo->band);
err |= __put_user(kinfo->si_fd, &uinfo->fd);
break;
case __SI_FAULT:
err |= __put_user((long)kinfo->si_addr, &uinfo->addr);
#ifdef __ARCH_SI_TRAPNO
err |= __put_user(kinfo->si_trapno, &uinfo->trapno);
#endif
break;
case __SI_CHLD:
err |= __put_user(kinfo->si_pid, &uinfo->pid);
err |= __put_user(kinfo->si_uid, &uinfo->uid);
err |= __put_user(kinfo->si_status, &uinfo->status);
err |= __put_user(kinfo->si_utime, &uinfo->utime);
err |= __put_user(kinfo->si_stime, &uinfo->stime);
break;
case __SI_RT: /* This is not generated by the kernel as of now. */
case __SI_MESGQ: /* But this is */
err |= __put_user(kinfo->si_pid, &uinfo->pid);
err |= __put_user(kinfo->si_uid, &uinfo->uid);
err |= __put_user((long)kinfo->si_ptr, &uinfo->svptr);
break;
default: /* this is just in case for now ... */
err |= __put_user(kinfo->si_pid, &uinfo->pid);
err |= __put_user(kinfo->si_uid, &uinfo->uid);
break;
}

return err ? -EFAULT: sizeof(*uinfo);
}

/*
* 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;
ssize_t res = 0;
int locked, signo;
siginfo_t info;
struct signalfd_lockctx lk;
DECLARE_WAITQUEUE(wait, current);

if (count < sizeof(struct signalfd_siginfo))
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;
}

static const struct file_operations signalfd_fops = {
.release = signalfd_release,
.poll = signalfd_poll,
.read = signalfd_read,
};

/*
* Create a file descriptor that is associated with our signal
* state. We can pass it around to others if we want to, but
* it will always be _our_ signal state.
*/
asmlinkage long sys_signalfd(int ufd, sigset_t __user *user_mask, size_t sizemask)
{
int error;
sigset_t sigmask;
struct signalfd_ctx *ctx;
struct sighand_struct *sighand;
struct file *file;
struct inode *inode;
struct signalfd_lockctx lk;

if (sizemask != sizeof(sigset_t) ||
copy_from_user(&sigmask, user_mask, sizeof(sigmask)))
return error = -EINVAL;
sigdelsetmask(&sigmask, sigmask(SIGKILL) | sigmask(SIGSTOP));
signotset(&sigmask);

if (ufd == -1) {
ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;

init_waitqueue_head(&ctx->wqh);
ctx->sigmask = sigmask;
ctx->tsk = current;

sighand = current->sighand;
/*
* Add this fd to the list of signal listeners.
*/
spin_lock_irq(&sighand->siglock);
list_add_tail(&ctx->lnk, &sighand->signalfd_list);
spin_unlock_irq(&sighand->siglock);

/*
* When we call this, the initialization must be complete, since
* anon_inode_getfd() will install the fd.
*/
error = anon_inode_getfd(&ufd, &inode, &file, "[signalfd]",
&signalfd_fops, ctx);
if (error)
goto err_fdalloc;
} else {
file = fget(ufd);
if (!file)
return -EBADF;
ctx = file->private_data;
if (file->f_op != &signalfd_fops) {
fput(file);
return -EINVAL;
}
/*
* We need to be prepared of the fact that the sighand this fd
* is attached to, has been detched. In that case signalfd_lock()
* will return 0, and we'll just skip setting the new mask.
*/
if (signalfd_lock(ctx, &lk)) {
ctx->sigmask = sigmask;
signalfd_unlock(&lk);
}
wake_up(&ctx->wqh);
fput(file);
}

return ufd;

err_fdalloc:
signalfd_cleanup(ctx);
return error;
}

Loading

0 comments on commit 91e4b4a

Please sign in to comment.