]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
netfs: Fix potential deadlock in write-through mode
authorDavid Howells <dhowells@redhat.com>
Tue, 12 May 2026 12:33:51 +0000 (13:33 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 1 Jun 2026 15:46:27 +0000 (17:46 +0200)
[ Upstream commit b6a4ae1634b3ad2aaa05222e53d36da532852faf ]

Fix netfs_advance_writethrough() to always unlock the supplied folio and to
mark it dirty if it isn't yet written to the end.  Unfortunately, it can't
be marked for writeback until the folio is done with as that may cause a
deadlock against mmapped reads and writes.

Even though it has been marked dirty, premature writeback can't occur as
the caller is holding both inode->i_rwsem (which will prevent concurrent
truncation, fallocation, DIO and other writes) and ictx->wb_lock (which
will cause flushing to wait and writeback to skip or wait).

Note that this may be easier to deal with once the queuing of folios is
split from the generation of subrequests.

Fixes: 288ace2f57c9 ("netfs: New writeback implementation")
Closes: https://sashiko.dev/#/patchset/20260427154639.180684-1-dhowells%40redhat.com
Signed-off-by: David Howells <dhowells@redhat.com>
Link: https://patch.msgid.link/20260512123404.719402-15-dhowells@redhat.com
cc: Paulo Alcantara <pc@manguebit.org>
cc: netfs@lists.linux.dev
cc: linux-fsdevel@vger.kernel.org
Signed-off-by: Christian Brauner <brauner@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
fs/netfs/write_issue.c

index b7830a15ae40f361afd52422c2b84264ec53406c..2789ac4c80272b3e416eb9d6b4c85d306f0a73a8 100644 (file)
@@ -402,12 +402,7 @@ static int netfs_write_folio(struct netfs_io_request *wreq,
        if (streamw)
                netfs_issue_write(wreq, cache);
 
-       /* Flip the page to the writeback state and unlock.  If we're called
-        * from write-through, then the page has already been put into the wb
-        * state.
-        */
-       if (wreq->origin == NETFS_WRITEBACK)
-               folio_start_writeback(folio);
+       folio_start_writeback(folio);
        folio_unlock(folio);
 
        if (fgroup == NETFS_FOLIO_COPY_TO_CACHE) {
@@ -632,29 +627,41 @@ int netfs_advance_writethrough(struct netfs_io_request *wreq, struct writeback_c
                               struct folio *folio, size_t copied, bool to_page_end,
                               struct folio **writethrough_cache)
 {
+       int ret;
+
        _enter("R=%x ic=%zu ws=%u cp=%zu tp=%u",
               wreq->debug_id, wreq->iter.count, wreq->wsize, copied, to_page_end);
 
-       if (!*writethrough_cache) {
-               if (folio_test_dirty(folio))
-                       /* Sigh.  mmap. */
-                       folio_clear_dirty_for_io(folio);
+       /* The folio is locked. */
 
+       if (*writethrough_cache != folio) {
+               if (*writethrough_cache) {
+                       /* Did the folio get moved? */
+                       folio_put(*writethrough_cache);
+                       *writethrough_cache = NULL;
+               }
                /* We can make multiple writes to the folio... */
-               folio_start_writeback(folio);
                if (wreq->len == 0)
                        trace_netfs_folio(folio, netfs_folio_trace_wthru);
                else
                        trace_netfs_folio(folio, netfs_folio_trace_wthru_plus);
                *writethrough_cache = folio;
+               folio_get(folio);
        }
 
        wreq->len += copied;
-       if (!to_page_end)
+
+       if (!to_page_end) {
+               folio_mark_dirty(folio);
+               folio_unlock(folio);
                return 0;
+       }
 
+       ret = netfs_write_folio(wreq, wbc, folio);
+       folio_put(*writethrough_cache);
        *writethrough_cache = NULL;
-       return netfs_write_folio(wreq, wbc, folio);
+       wreq->submitted = wreq->len;
+       return ret;
 }
 
 /*
@@ -668,8 +675,12 @@ int netfs_end_writethrough(struct netfs_io_request *wreq, struct writeback_contr
 
        _enter("R=%x", wreq->debug_id);
 
-       if (writethrough_cache)
+       if (writethrough_cache) {
+               folio_lock(writethrough_cache);
                netfs_write_folio(wreq, wbc, writethrough_cache);
+               folio_put(writethrough_cache);
+               wreq->submitted = wreq->len;
+       }
 
        netfs_end_issue_write(wreq);