Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 65087
b: refs/heads/master
c: b8fceee
h: refs/heads/master
i:
  65085: 4879c70
  65083: 7c8be71
  65079: 67488d8
  65071: d1b4a34
  65055: ffafd8d
  65023: d28d8c3
v: v3
  • Loading branch information
Davide Libenzi authored and Linus Torvalds committed Sep 20, 2007
1 parent 94dd948 commit 7b347bd
Show file tree
Hide file tree
Showing 9 changed files with 40 additions and 218 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: 9db619e66503494e41159de3c76fafabe80d016b
refs/heads/master: b8fceee17a310f189188599a8fa5e9beaff57eb0
3 changes: 0 additions & 3 deletions trunk/fs/exec.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
#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 @@ -784,7 +783,6 @@ static int de_thread(struct task_struct *tsk)
* and we can just re-use it all.
*/
if (atomic_read(&oldsighand->count) <= 1) {
signalfd_detach(tsk);
exit_itimers(sig);
return 0;
}
Expand Down Expand Up @@ -923,7 +921,6 @@ static int de_thread(struct task_struct *tsk)
sig->flags = 0;

no_thread_group:
signalfd_detach(tsk);
exit_itimers(sig);
if (leader)
release_task(leader);
Expand Down
190 changes: 29 additions & 161 deletions trunk/fs/signalfd.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
* Now using anonymous inode source.
* Thanks to Oleg Nesterov for useful code review and suggestions.
* More comments and suggestions from Arnd Bergmann.
* Sat May 19, 2007: Davi E. M. Arnaut <davi@haxent.com.br>
* Sat May 19, 2007: Davi E. M. Arnaut <davi@haxent.com.br>
* Retrieve multiple signals with one read() call
* Sun Jul 15, 2007: Davide Libenzi <davidel@xmailserver.org>
* Attach to the sighand only during read() and poll().
*/

#include <linux/file.h>
Expand All @@ -27,126 +29,28 @@
#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)
return 0;

if (!ctx->tsk) {
unlock_task_sighand(lk->tsk, &lk->flags);
return 0;
}

if (lk->tsk->tgid == current->tgid)
lk->tsk = current;

return 1;
}

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);
kfree(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);
poll_wait(file, &current->sighand->signalfd_wqh, wait);

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

return events;
}
Expand Down Expand Up @@ -219,59 +123,46 @@ static ssize_t signalfd_dequeue(struct signalfd_ctx *ctx, siginfo_t *info,
int nonblock)
{
ssize_t ret;
struct signalfd_lockctx lk;
DECLARE_WAITQUEUE(wait, current);

if (!signalfd_lock(ctx, &lk))
return 0;

ret = dequeue_signal(lk.tsk, &ctx->sigmask, info);
spin_lock_irq(&current->sighand->siglock);
ret = dequeue_signal(current, &ctx->sigmask, info);
switch (ret) {
case 0:
if (!nonblock)
break;
ret = -EAGAIN;
default:
signalfd_unlock(&lk);
spin_unlock_irq(&current->sighand->siglock);
return ret;
}

add_wait_queue(&ctx->wqh, &wait);
add_wait_queue(&current->sighand->signalfd_wqh, &wait);
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
ret = dequeue_signal(lk.tsk, &ctx->sigmask, info);
signalfd_unlock(&lk);
ret = dequeue_signal(current, &ctx->sigmask, info);
if (ret != 0)
break;
if (signal_pending(current)) {
ret = -ERESTARTSYS;
break;
}
spin_unlock_irq(&current->sighand->siglock);
schedule();
ret = signalfd_lock(ctx, &lk);
if (unlikely(!ret)) {
/*
* 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
* because ->sighand will be long gone.
*/
break;
}
spin_lock_irq(&current->sighand->siglock);
}
spin_unlock_irq(&current->sighand->siglock);

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

return ret;
}

/*
* 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".
* Returns a multiple of the size of a "struct signalfd_siginfo", or a negative
* error code. 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)
Expand All @@ -287,7 +178,6 @@ static ssize_t signalfd_read(struct file *file, char __user *buf, size_t count,
return -EINVAL;

siginfo = (struct signalfd_siginfo __user *) buf;

do {
ret = signalfd_dequeue(ctx, &info, nonblock);
if (unlikely(ret <= 0))
Expand All @@ -300,7 +190,7 @@ static ssize_t signalfd_read(struct file *file, char __user *buf, size_t count,
nonblock = 1;
} while (--count);

return total ? total : ret;
return total ? total: ret;
}

static const struct file_operations signalfd_fops = {
Expand All @@ -309,20 +199,13 @@ static const struct file_operations signalfd_fops = {
.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)))
Expand All @@ -335,17 +218,7 @@ asmlinkage long sys_signalfd(int ufd, sigset_t __user *user_mask, size_t sizemas
if (!ctx)
return -ENOMEM;

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

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
Expand All @@ -364,23 +237,18 @@ asmlinkage long sys_signalfd(int ufd, sigset_t __user *user_mask, size_t sizemas
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);
spin_lock_irq(&current->sighand->siglock);
ctx->sigmask = sigmask;
spin_unlock_irq(&current->sighand->siglock);

wake_up(&current->sighand->signalfd_wqh);
fput(file);
}

return ufd;

err_fdalloc:
signalfd_cleanup(ctx);
kfree(ctx);
return error;
}

2 changes: 1 addition & 1 deletion trunk/include/linux/init_task.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ extern struct nsproxy init_nsproxy;
.count = ATOMIC_INIT(1), \
.action = { { { .sa_handler = NULL, } }, }, \
.siglock = __SPIN_LOCK_UNLOCKED(sighand.siglock), \
.signalfd_list = LIST_HEAD_INIT(sighand.signalfd_list), \
.signalfd_wqh = __WAIT_QUEUE_HEAD_INITIALIZER(sighand.signalfd_wqh), \
}

extern struct group_info init_groups;
Expand Down
2 changes: 1 addition & 1 deletion trunk/include/linux/sched.h
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ struct sighand_struct {
atomic_t count;
struct k_sigaction action[_NSIG];
spinlock_t siglock;
struct list_head signalfd_list;
wait_queue_head_t signalfd_wqh;
};

struct pacct_struct {
Expand Down
Loading

0 comments on commit 7b347bd

Please sign in to comment.