mirror of
https://github.com/torvalds/linux.git
synced 2026-05-31 02:24:24 +02:00
netfs: Fix potential deadlock in write-through mode
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: 288ace2f57 ("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>
This commit is contained in:
parent
7b4dcf1b94
commit
b6a4ae1634
|
|
@ -414,12 +414,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) {
|
||||
|
|
@ -647,29 +642,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->buffer.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)
|
||||
return 0;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -683,8 +690,12 @@ ssize_t netfs_end_writethrough(struct netfs_io_request *wreq, struct writeback_c
|
|||
|
||||
_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);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user