X-Git-Url: http://pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=fs%2Fsignalfd.c;h=fb7f7e8034df512db10978276d7c2cf8f9cc0a75;hb=e07dd2ad305f6b29b47d713600aa8b722ef2a9f7;hp=7cfeab412b45ec4ef381caef24a721e966a4f3ad;hpb=fba2afaaec790dc5ab4ae8827972f342211bbb86;p=linux-2.6-omap-h63xx.git diff --git a/fs/signalfd.c b/fs/signalfd.c index 7cfeab412b4..fb7f7e8034d 100644 --- a/fs/signalfd.c +++ b/fs/signalfd.c @@ -11,6 +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 + * Retrieve multiple signals with one read() call + * Sun Jul 15, 2007: Davide Libenzi + * Attach to the sighand only during read() and poll(). */ #include @@ -25,96 +29,12 @@ #include 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); + kfree(file->private_data); return 0; } @@ -122,22 +42,15 @@ 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, ¤t->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 (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(¤t->sighand->siglock); + if (next_signal(¤t->pending, &ctx->sigmask) || + next_signal(¤t->signal->shared_pending, + &ctx->sigmask)) events |= POLLIN; + spin_unlock_irq(¤t->sighand->siglock); return events; } @@ -161,109 +74,123 @@ static int signalfd_copyinfo(struct signalfd_siginfo __user *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); + err |= __put_user(kinfo->si_signo, &uinfo->ssi_signo); + err |= __put_user(kinfo->si_errno, &uinfo->ssi_errno); + err |= __put_user((short) kinfo->si_code, &uinfo->ssi_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); + err |= __put_user(kinfo->si_pid, &uinfo->ssi_pid); + err |= __put_user(kinfo->si_uid, &uinfo->ssi_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); + err |= __put_user(kinfo->si_tid, &uinfo->ssi_tid); + err |= __put_user(kinfo->si_overrun, &uinfo->ssi_overrun); + err |= __put_user((long) kinfo->si_ptr, &uinfo->ssi_ptr); break; case __SI_POLL: - err |= __put_user(kinfo->si_band, &uinfo->band); - err |= __put_user(kinfo->si_fd, &uinfo->fd); + err |= __put_user(kinfo->si_band, &uinfo->ssi_band); + err |= __put_user(kinfo->si_fd, &uinfo->ssi_fd); break; case __SI_FAULT: - err |= __put_user((long)kinfo->si_addr, &uinfo->addr); + err |= __put_user((long) kinfo->si_addr, &uinfo->ssi_addr); #ifdef __ARCH_SI_TRAPNO - err |= __put_user(kinfo->si_trapno, &uinfo->trapno); + err |= __put_user(kinfo->si_trapno, &uinfo->ssi_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); + err |= __put_user(kinfo->si_pid, &uinfo->ssi_pid); + err |= __put_user(kinfo->si_uid, &uinfo->ssi_uid); + err |= __put_user(kinfo->si_status, &uinfo->ssi_status); + err |= __put_user(kinfo->si_utime, &uinfo->ssi_utime); + err |= __put_user(kinfo->si_stime, &uinfo->ssi_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); + err |= __put_user(kinfo->si_pid, &uinfo->ssi_pid); + err |= __put_user(kinfo->si_uid, &uinfo->ssi_uid); + err |= __put_user((long) kinfo->si_ptr, &uinfo->ssi_ptr); 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); + err |= __put_user(kinfo->si_pid, &uinfo->ssi_pid); + err |= __put_user(kinfo->si_uid, &uinfo->ssi_uid); break; } return err ? -EFAULT: sizeof(*uinfo); } +static ssize_t signalfd_dequeue(struct signalfd_ctx *ctx, siginfo_t *info, + int nonblock) +{ + ssize_t ret; + DECLARE_WAITQUEUE(wait, current); + + spin_lock_irq(¤t->sighand->siglock); + ret = dequeue_signal(current, &ctx->sigmask, info); + switch (ret) { + case 0: + if (!nonblock) + break; + ret = -EAGAIN; + default: + spin_unlock_irq(¤t->sighand->siglock); + return ret; + } + + add_wait_queue(¤t->sighand->signalfd_wqh, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + ret = dequeue_signal(current, &ctx->sigmask, info); + if (ret != 0) + break; + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + spin_unlock_irq(¤t->sighand->siglock); + schedule(); + spin_lock_irq(¤t->sighand->siglock); + } + spin_unlock_irq(¤t->sighand->siglock); + + remove_wait_queue(¤t->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) { struct signalfd_ctx *ctx = file->private_data; - ssize_t res = 0; - int locked, signo; + struct signalfd_siginfo __user *siginfo; + int nonblock = file->f_flags & O_NONBLOCK; + ssize_t ret, total = 0; siginfo_t info; - struct signalfd_lockctx lk; - DECLARE_WAITQUEUE(wait, current); - if (count < sizeof(struct signalfd_siginfo)) + count /= sizeof(struct signalfd_siginfo); + if (!count) 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; + 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 = { @@ -272,24 +199,17 @@ 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))) - return error = -EINVAL; + return -EINVAL; sigdelsetmask(&sigmask, sigmask(SIGKILL) | sigmask(SIGSTOP)); signotset(&sigmask); @@ -298,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; - - 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 @@ -327,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(¤t->sighand->siglock); + ctx->sigmask = sigmask; + spin_unlock_irq(¤t->sighand->siglock); + + wake_up(¤t->sighand->signalfd_wqh); fput(file); } return ufd; err_fdalloc: - signalfd_cleanup(ctx); + kfree(ctx); return error; }