diff --git a/mm/filemap.c b/mm/filemap.c index 2974691fdfad..fbaf0e4fc8e1 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -4169,17 +4169,6 @@ ssize_t generic_perform_write(struct kiocb *iocb, struct iov_iter *i) bytes = min(chunk - offset, bytes); balance_dirty_pages_ratelimited(mapping); - /* - * Bring in the user page that we will copy from _first_. - * Otherwise there's a nasty deadlock on copying from the - * same page as we're writing to, without it being marked - * up-to-date. - */ - if (unlikely(fault_in_iov_iter_readable(i, bytes) == bytes)) { - status = -EFAULT; - break; - } - if (fatal_signal_pending(current)) { status = -EINTR; break; @@ -4197,6 +4186,12 @@ ssize_t generic_perform_write(struct kiocb *iocb, struct iov_iter *i) if (mapping_writably_mapped(mapping)) flush_dcache_folio(folio); + /* + * Faults here on mmap()s can recurse into arbitrary + * filesystem code. Lots of locks are held that can + * deadlock. Use an atomic copy to avoid deadlocking + * in page fault handling. + */ copied = copy_folio_from_iter_atomic(folio, offset, bytes, i); flush_dcache_folio(folio); @@ -4222,6 +4217,16 @@ ssize_t generic_perform_write(struct kiocb *iocb, struct iov_iter *i) bytes = copied; goto retry; } + + /* + * 'folio' is now unlocked and faults on it can be + * handled. Ensure forward progress by trying to + * fault it in now. + */ + if (fault_in_iov_iter_readable(i, bytes) == bytes) { + status = -EFAULT; + break; + } } else { pos += status; written += status;