diff options
-rw-r--r-- | fs/signalfd.c | 120 |
1 files changed, 75 insertions, 45 deletions
diff --git a/fs/signalfd.c b/fs/signalfd.c index 7cfeab412b45..f1da89203a9a 100644 --- a/fs/signalfd.c +++ b/fs/signalfd.c | |||
@@ -11,6 +11,8 @@ | |||
11 | * Now using anonymous inode source. | 11 | * Now using anonymous inode source. |
12 | * Thanks to Oleg Nesterov for useful code review and suggestions. | 12 | * Thanks to Oleg Nesterov for useful code review and suggestions. |
13 | * More comments and suggestions from Arnd Bergmann. | 13 | * More comments and suggestions from Arnd Bergmann. |
14 | * Sat May 19, 2007: Davi E. M. Arnaut <davi@haxent.com.br> | ||
15 | * Retrieve multiple signals with one read() call | ||
14 | */ | 16 | */ |
15 | 17 | ||
16 | #include <linux/file.h> | 18 | #include <linux/file.h> |
@@ -206,6 +208,59 @@ static int signalfd_copyinfo(struct signalfd_siginfo __user *uinfo, | |||
206 | return err ? -EFAULT: sizeof(*uinfo); | 208 | return err ? -EFAULT: sizeof(*uinfo); |
207 | } | 209 | } |
208 | 210 | ||
211 | static ssize_t signalfd_dequeue(struct signalfd_ctx *ctx, siginfo_t *info, | ||
212 | int nonblock) | ||
213 | { | ||
214 | ssize_t ret; | ||
215 | struct signalfd_lockctx lk; | ||
216 | DECLARE_WAITQUEUE(wait, current); | ||
217 | |||
218 | if (!signalfd_lock(ctx, &lk)) | ||
219 | return 0; | ||
220 | |||
221 | ret = dequeue_signal(lk.tsk, &ctx->sigmask, info); | ||
222 | switch (ret) { | ||
223 | case 0: | ||
224 | if (!nonblock) | ||
225 | break; | ||
226 | ret = -EAGAIN; | ||
227 | default: | ||
228 | signalfd_unlock(&lk); | ||
229 | return ret; | ||
230 | } | ||
231 | |||
232 | add_wait_queue(&ctx->wqh, &wait); | ||
233 | for (;;) { | ||
234 | set_current_state(TASK_INTERRUPTIBLE); | ||
235 | ret = dequeue_signal(lk.tsk, &ctx->sigmask, info); | ||
236 | signalfd_unlock(&lk); | ||
237 | if (ret != 0) | ||
238 | break; | ||
239 | if (signal_pending(current)) { | ||
240 | ret = -ERESTARTSYS; | ||
241 | break; | ||
242 | } | ||
243 | schedule(); | ||
244 | ret = signalfd_lock(ctx, &lk); | ||
245 | if (unlikely(!ret)) { | ||
246 | /* | ||
247 | * Let the caller read zero byte, ala socket | ||
248 | * recv() when the peer disconnect. This test | ||
249 | * must be done before doing a dequeue_signal(), | ||
250 | * because if the sighand has been orphaned, | ||
251 | * the dequeue_signal() call is going to crash | ||
252 | * because ->sighand will be long gone. | ||
253 | */ | ||
254 | break; | ||
255 | } | ||
256 | } | ||
257 | |||
258 | remove_wait_queue(&ctx->wqh, &wait); | ||
259 | __set_current_state(TASK_RUNNING); | ||
260 | |||
261 | return ret; | ||
262 | } | ||
263 | |||
209 | /* | 264 | /* |
210 | * Returns either the size of a "struct signalfd_siginfo", or zero if the | 265 | * Returns either the size of a "struct signalfd_siginfo", or zero if the |
211 | * sighand we are attached to, has been orphaned. The "count" parameter | 266 | * sighand we are attached to, has been orphaned. The "count" parameter |
@@ -215,55 +270,30 @@ static ssize_t signalfd_read(struct file *file, char __user *buf, size_t count, | |||
215 | loff_t *ppos) | 270 | loff_t *ppos) |
216 | { | 271 | { |
217 | struct signalfd_ctx *ctx = file->private_data; | 272 | struct signalfd_ctx *ctx = file->private_data; |
218 | ssize_t res = 0; | 273 | struct signalfd_siginfo __user *siginfo; |
219 | int locked, signo; | 274 | int nonblock = file->f_flags & O_NONBLOCK; |
275 | ssize_t ret, total = 0; | ||
220 | siginfo_t info; | 276 | siginfo_t info; |
221 | struct signalfd_lockctx lk; | ||
222 | DECLARE_WAITQUEUE(wait, current); | ||
223 | 277 | ||
224 | if (count < sizeof(struct signalfd_siginfo)) | 278 | count /= sizeof(struct signalfd_siginfo); |
279 | if (!count) | ||
225 | return -EINVAL; | 280 | return -EINVAL; |
226 | locked = signalfd_lock(ctx, &lk); | ||
227 | if (!locked) | ||
228 | return 0; | ||
229 | res = -EAGAIN; | ||
230 | signo = dequeue_signal(lk.tsk, &ctx->sigmask, &info); | ||
231 | if (signo == 0 && !(file->f_flags & O_NONBLOCK)) { | ||
232 | add_wait_queue(&ctx->wqh, &wait); | ||
233 | for (;;) { | ||
234 | set_current_state(TASK_INTERRUPTIBLE); | ||
235 | signo = dequeue_signal(lk.tsk, &ctx->sigmask, &info); | ||
236 | if (signo != 0) | ||
237 | break; | ||
238 | if (signal_pending(current)) { | ||
239 | res = -ERESTARTSYS; | ||
240 | break; | ||
241 | } | ||
242 | signalfd_unlock(&lk); | ||
243 | schedule(); | ||
244 | locked = signalfd_lock(ctx, &lk); | ||
245 | if (unlikely(!locked)) { | ||
246 | /* | ||
247 | * Let the caller read zero byte, ala socket | ||
248 | * recv() when the peer disconnect. This test | ||
249 | * must be done before doing a dequeue_signal(), | ||
250 | * because if the sighand has been orphaned, | ||
251 | * the dequeue_signal() call is going to crash. | ||
252 | */ | ||
253 | res = 0; | ||
254 | break; | ||
255 | } | ||
256 | } | ||
257 | remove_wait_queue(&ctx->wqh, &wait); | ||
258 | __set_current_state(TASK_RUNNING); | ||
259 | } | ||
260 | if (likely(locked)) | ||
261 | signalfd_unlock(&lk); | ||
262 | if (likely(signo)) | ||
263 | res = signalfd_copyinfo((struct signalfd_siginfo __user *) buf, | ||
264 | &info); | ||
265 | 281 | ||
266 | return res; | 282 | siginfo = (struct signalfd_siginfo __user *) buf; |
283 | |||
284 | do { | ||
285 | ret = signalfd_dequeue(ctx, &info, nonblock); | ||
286 | if (unlikely(ret <= 0)) | ||
287 | break; | ||
288 | ret = signalfd_copyinfo(siginfo, &info); | ||
289 | if (ret < 0) | ||
290 | break; | ||
291 | siginfo++; | ||
292 | total += ret; | ||
293 | nonblock = 1; | ||
294 | } while (--count); | ||
295 | |||
296 | return total ? total : ret; | ||
267 | } | 297 | } |
268 | 298 | ||
269 | static const struct file_operations signalfd_fops = { | 299 | static const struct file_operations signalfd_fops = { |