| From 296291cdd1629c308114504b850dc343eabc2782 Mon Sep 17 00:00:00 2001 |
| From: Jan Kara <jack@suse.com> |
| Date: Thu, 22 Oct 2015 13:32:21 -0700 |
| Subject: mm: make sendfile(2) killable |
| |
| From: Jan Kara <jack@suse.com> |
| |
| commit 296291cdd1629c308114504b850dc343eabc2782 upstream. |
| |
| Currently a simple program below issues a sendfile(2) system call which |
| takes about 62 days to complete in my test KVM instance. |
| |
| int fd; |
| off_t off = 0; |
| |
| fd = open("file", O_RDWR | O_TRUNC | O_SYNC | O_CREAT, 0644); |
| ftruncate(fd, 2); |
| lseek(fd, 0, SEEK_END); |
| sendfile(fd, fd, &off, 0xfffffff); |
| |
| Now you should not ask kernel to do a stupid stuff like copying 256MB in |
| 2-byte chunks and call fsync(2) after each chunk but if you do, sysadmin |
| should have a way to stop you. |
| |
| We actually do have a check for fatal_signal_pending() in |
| generic_perform_write() which triggers in this path however because we |
| always succeed in writing something before the check is done, we return |
| value > 0 from generic_perform_write() and thus the information about |
| signal gets lost. |
| |
| Fix the problem by doing the signal check before writing anything. That |
| way generic_perform_write() returns -EINTR, the error gets propagated up |
| and the sendfile loop terminates early. |
| |
| Signed-off-by: Jan Kara <jack@suse.com> |
| Reported-by: Dmitry Vyukov <dvyukov@google.com> |
| Cc: Al Viro <viro@ZenIV.linux.org.uk> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| mm/filemap.c | 9 +++++---- |
| 1 file changed, 5 insertions(+), 4 deletions(-) |
| |
| --- a/mm/filemap.c |
| +++ b/mm/filemap.c |
| @@ -2340,6 +2340,11 @@ again: |
| break; |
| } |
| |
| + if (fatal_signal_pending(current)) { |
| + status = -EINTR; |
| + break; |
| + } |
| + |
| status = a_ops->write_begin(file, mapping, pos, bytes, flags, |
| &page, &fsdata); |
| if (unlikely(status)) |
| @@ -2380,10 +2385,6 @@ again: |
| written += copied; |
| |
| balance_dirty_pages_ratelimited(mapping); |
| - if (fatal_signal_pending(current)) { |
| - status = -EINTR; |
| - break; |
| - } |
| } while (iov_iter_count(i)); |
| |
| return written ? written : status; |