diff options
author | Oleg Nesterov <oleg@redhat.com> | 2013-04-30 18:28:15 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-04-30 20:04:06 -0400 |
commit | 528f827ee0bb508af5c9466562f474675540473e (patch) | |
tree | 9c13212aa45b8e08fc3b8016407c6ee0f8603604 /fs | |
parent | acdedd99b0f3bff9b4bb2103a6b1268c03d1f963 (diff) |
coredump: introduce dump_interrupted()
By discussion with Mandeep.
Change dump_write(), dump_seek() and do_coredump() to check
signal_pending() and abort if it is true. dump_seek() does this only
before f_op->llseek(), otherwise it relies on dump_write().
We need this change to ensure that the coredump won't delay suspend, and
to ensure it reacts to SIGKILL "quickly enough", a core dump can take a
lot of time. In particular this can help oom-killer.
We add the new trivial helper, dump_interrupted() to add the comments and
to simplify the potential freezer changes. Perhaps it will have more
callers.
Ideally it should do try_to_freeze() but then we need the unpleasant
changes in dump_write() and wait_for_dump_helpers(). It is not trivial to
change dump_write() to restart if f_op->write() fails because of
freezing(). We need to handle the short writes, we need to clear
TIF_SIGPENDING (and we can't rely on recalc_sigpending() unless we change
it to check PF_DUMPCORE). And if the buggy f_op->write() sets
TIF_SIGPENDING we can not distinguish this case from the race with
freeze_task() + __thaw_task().
So we simply accept the fact that the freezer can truncate a core-dump but
at least you can reliably suspend. Hopefully we can tolerate this
unlikely case and the necessary complications doesn't worth a trouble.
But if we decide to make the coredumping freezable later we can do this on
top of this change.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: Mandeep Singh Baines <msb@chromium.org>
Cc: Neil Horman <nhorman@redhat.com>
Cc: "Rafael J. Wysocki" <rjw@sisk.pl>
Cc: Tejun Heo <tj@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/coredump.c | 20 |
1 files changed, 17 insertions, 3 deletions
diff --git a/fs/coredump.c b/fs/coredump.c index acc4448c28e7..aa8ac69a548f 100644 --- a/fs/coredump.c +++ b/fs/coredump.c | |||
@@ -418,6 +418,17 @@ static void coredump_finish(struct mm_struct *mm, bool core_dumped) | |||
418 | mm->core_state = NULL; | 418 | mm->core_state = NULL; |
419 | } | 419 | } |
420 | 420 | ||
421 | static bool dump_interrupted(void) | ||
422 | { | ||
423 | /* | ||
424 | * SIGKILL or freezing() interrupt the coredumping. Perhaps we | ||
425 | * can do try_to_freeze() and check __fatal_signal_pending(), | ||
426 | * but then we need to teach dump_write() to restart and clear | ||
427 | * TIF_SIGPENDING. | ||
428 | */ | ||
429 | return signal_pending(current); | ||
430 | } | ||
431 | |||
421 | static void wait_for_dump_helpers(struct file *file) | 432 | static void wait_for_dump_helpers(struct file *file) |
422 | { | 433 | { |
423 | struct pipe_inode_info *pipe; | 434 | struct pipe_inode_info *pipe; |
@@ -641,7 +652,7 @@ void do_coredump(siginfo_t *siginfo) | |||
641 | goto close_fail; | 652 | goto close_fail; |
642 | if (displaced) | 653 | if (displaced) |
643 | put_files_struct(displaced); | 654 | put_files_struct(displaced); |
644 | core_dumped = binfmt->core_dump(&cprm); | 655 | core_dumped = !dump_interrupted() && binfmt->core_dump(&cprm); |
645 | 656 | ||
646 | if (ispipe && core_pipe_limit) | 657 | if (ispipe && core_pipe_limit) |
647 | wait_for_dump_helpers(cprm.file); | 658 | wait_for_dump_helpers(cprm.file); |
@@ -669,7 +680,9 @@ fail: | |||
669 | */ | 680 | */ |
670 | int dump_write(struct file *file, const void *addr, int nr) | 681 | int dump_write(struct file *file, const void *addr, int nr) |
671 | { | 682 | { |
672 | return access_ok(VERIFY_READ, addr, nr) && file->f_op->write(file, addr, nr, &file->f_pos) == nr; | 683 | return !dump_interrupted() && |
684 | access_ok(VERIFY_READ, addr, nr) && | ||
685 | file->f_op->write(file, addr, nr, &file->f_pos) == nr; | ||
673 | } | 686 | } |
674 | EXPORT_SYMBOL(dump_write); | 687 | EXPORT_SYMBOL(dump_write); |
675 | 688 | ||
@@ -678,7 +691,8 @@ int dump_seek(struct file *file, loff_t off) | |||
678 | int ret = 1; | 691 | int ret = 1; |
679 | 692 | ||
680 | if (file->f_op->llseek && file->f_op->llseek != no_llseek) { | 693 | if (file->f_op->llseek && file->f_op->llseek != no_llseek) { |
681 | if (file->f_op->llseek(file, off, SEEK_CUR) < 0) | 694 | if (dump_interrupted() || |
695 | file->f_op->llseek(file, off, SEEK_CUR) < 0) | ||
682 | return 0; | 696 | return 0; |
683 | } else { | 697 | } else { |
684 | char *buf = (char *)get_zeroed_page(GFP_KERNEL); | 698 | char *buf = (char *)get_zeroed_page(GFP_KERNEL); |