Mirror the changes made to afs to partially revert the changes made by: acc8d8588cb7e3e64b0d2fa611dad06574cd67b1. "afs: convert afs_writepages_region() to use filemap_get_folios_tag()" that were then mirrored into cifs. The issue is that filemap_get_folios_tag() gets a batch of pages at a time, and then cifs_writepages_region() goes through them one at a time, extends each into an operation with as many pages as will fit using the loop in cifs_extend_writeback() and submits it - but, in the common case, this means that the other pages in the batch already got annexed and processed in cifs_extend_writeback() and we end up doing duplicate processing. Switching to write_cache_pages() isn't an immediate substitute as that doesn't take account of PG_fscache (and this bit is used in other ways by other filesystems). So go back to finding the next folio from the VM one at a time and then extending the op onwards. Fixes: 3822a7c40997 ("Merge tag 'mm-stable-2023-02-20-13-37' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm") Signed-off-by: David Howells <dhowells@xxxxxxxxxx> cc: Steve French <sfrench@xxxxxxxxx> cc: Linus Torvalds <torvalds@xxxxxxxxxxxxxxxxxxxx> cc: Shyam Prasad N <nspmangalore@xxxxxxxxx> cc: Rohith Surabattula <rohiths.msft@xxxxxxxxx> cc: Jeff Layton <jlayton@xxxxxxxxxx> cc: Paulo Alcantara <pc@xxxxxx> cc: "Vishal Moola (Oracle)" <vishal.moola@xxxxxxxxx> cc: "Matthew Wilcox (Oracle)" <willy@xxxxxxxxxxxxx> cc: Andrew Morton <akpm@xxxxxxxxxxxxxxxxxxxx> cc: linux-cifs@xxxxxxxxxxxxxxx cc: linux-mm@xxxxxxxxx Link: https://lore.kernel.org/r/2214157.1677250083@xxxxxxxxxxxxxxxxxxxxxx/ --- fs/cifs/file.c | 115 +++++++++++++++++++++---------------------------- 1 file changed, 49 insertions(+), 66 deletions(-) diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 4d4a2d82636d..a3e89e741b42 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -2864,93 +2864,76 @@ static int cifs_writepages_region(struct address_space *mapping, struct writeback_control *wbc, loff_t start, loff_t end, loff_t *_next) { - struct folio_batch fbatch; + struct folio *folio; + ssize_t ret; int skips = 0; - folio_batch_init(&fbatch); do { - int nr; pgoff_t index = start / PAGE_SIZE; - nr = filemap_get_folios_tag(mapping, &index, end / PAGE_SIZE, - PAGECACHE_TAG_DIRTY, &fbatch); - if (!nr) + folio = filemap_get_folio_tag(mapping, &index, end / PAGE_SIZE, + PAGECACHE_TAG_DIRTY); + if (!folio) break; - for (int i = 0; i < nr; i++) { - ssize_t ret; - struct folio *folio = fbatch.folios[i]; - -redo_folio: - start = folio_pos(folio); /* May regress with THPs */ + start = folio_pos(folio); /* May regress with THPs */ - /* At this point we hold neither the i_pages lock nor the - * page lock: the page may be truncated or invalidated - * (changing page->mapping to NULL), or even swizzled - * back from swapper_space to tmpfs file mapping - */ - if (wbc->sync_mode != WB_SYNC_NONE) { - ret = folio_lock_killable(folio); - if (ret < 0) - goto write_error; - } else { - if (!folio_trylock(folio)) - goto skip_write; + /* At this point we hold neither the i_pages lock nor the + * page lock: the page may be truncated or invalidated + * (changing page->mapping to NULL), or even swizzled + * back from swapper_space to tmpfs file mapping + */ + if (wbc->sync_mode != WB_SYNC_NONE) { + ret = folio_lock_killable(folio); + if (ret < 0) { + folio_put(folio); + return ret; } - - if (folio_mapping(folio) != mapping || - !folio_test_dirty(folio)) { - start += folio_size(folio); - folio_unlock(folio); - continue; + } else { + if (!folio_trylock(folio)) { + folio_put(folio); + return 0; } + } - if (folio_test_writeback(folio) || - folio_test_fscache(folio)) { - folio_unlock(folio); - if (wbc->sync_mode == WB_SYNC_NONE) - goto skip_write; + if (folio_mapping(folio) != mapping || + !folio_test_dirty(folio)) { + start += folio_size(folio); + folio_unlock(folio); + folio_put(folio); + continue; + } + if (folio_test_writeback(folio) || + folio_test_fscache(folio)) { + folio_unlock(folio); + if (wbc->sync_mode != WB_SYNC_NONE) { folio_wait_writeback(folio); #ifdef CONFIG_CIFS_FSCACHE folio_wait_fscache(folio); #endif - goto redo_folio; + } else { + start += folio_size(folio); } - - if (!folio_clear_dirty_for_io(folio)) - /* We hold the page lock - it should've been dirty. */ - WARN_ON(1); - - ret = cifs_write_back_from_locked_folio(mapping, wbc, folio, start, end); - if (ret < 0) - goto write_error; - - start += ret; - continue; - -write_error: - folio_batch_release(&fbatch); - *_next = start; - return ret; - -skip_write: - /* - * Too many skipped writes, or need to reschedule? - * Treat it as a write error without an error code. - */ - if (skips >= 5 || need_resched()) { - ret = 0; - goto write_error; + folio_put(folio); + if (wbc->sync_mode == WB_SYNC_NONE) { + if (skips >= 5 || need_resched()) + break; + skips++; } - - /* Otherwise, just skip that folio and go on to the next */ - skips++; - start += folio_size(folio); continue; } - folio_batch_release(&fbatch); + if (!folio_clear_dirty_for_io(folio)) + /* We hold the page lock - it should've been dirty. */ + WARN_ON(1); + + ret = cifs_write_back_from_locked_folio(mapping, wbc, folio, start, end); + folio_put(folio); + if (ret < 0) + return ret; + + start += ret; cond_resched(); } while (wbc->nr_to_write > 0);