[PATCH 7/7] Remove folio_launder()

"Matthew Wilcox (Oracle)" <[email protected]>
Newsgroups org.kernel.vger.linux-block,dev.linux.lists.fuse-devel,org.kernel.vger.linux-btrfs,org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-nfs,org.kvack.linux-mm
Message-ID <[email protected]>
When we do direct I/O, we need to evict any page cache that overlaps
the range in the file.  First we write back any dirty folios in the
range, then we lock each folio and remove it from the CPU page tables.
This leaves a wide window for userspace to re-dirty the folio by storing
to a shared writable mmap.

Many filesystems respond to this situation by failing the call to
release_folio(), but some try to writeback the dirty folio again, formerly
in their release_folio() method and now in their launder_folio() method.

Remove this inconsistency between filesystems by checking
whether the folio is dirty in the VFS and failing the call to
folio_unmap_invalidate().  Since we hold the folio locked and unmapped
at this time, there is no way to dirty the folio after this point.

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
---
 Documentation/filesystems/locking.rst |  8 --------
 Documentation/filesystems/vfs.rst     |  6 ------
 include/linux/fs.h                    |  1 -
 mm/truncate.c                         | 15 ++-------------
 4 files changed, 2 insertions(+), 28 deletions(-)

diff --git a/Documentation/filesystems/locking.rst b/Documentation/filesystems/locking.rst
index 08d01bc62c31..fd11f5c5d91e 100644
--- a/Documentation/filesystems/locking.rst
+++ b/Documentation/filesystems/locking.rst
@@ -261,7 +261,6 @@ prototypes::
 	int (*direct_IO)(struct kiocb *, struct iov_iter *iter);
 	int (*migrate_folio)(struct address_space *, struct folio *dst,
 			struct folio *src, enum migrate_mode);
-	int (*launder_folio)(struct folio *);
 	bool (*is_partially_uptodate)(struct folio *, size_t from, size_t count);
 	int (*error_remove_folio)(struct address_space *, struct folio *);
 	int (*swap_activate)(struct swap_info_struct *sis, struct file *f, sector_t *span)
@@ -286,7 +285,6 @@ release_folio:		yes
 free_folio:		yes
 direct_IO:
 migrate_folio:		yes (both)
-launder_folio:		yes
 is_partially_uptodate:	yes
 error_remove_folio:	yes
 swap_activate:		no
@@ -344,12 +342,6 @@ try_to_free_buffers().
 ->free_folio() is called when the kernel has dropped the folio
 from the page cache.
 
-->launder_folio() may be called prior to releasing a folio if
-it is still found to be dirty. It returns zero if the folio was successfully
-cleaned, or an error value if not. Note that in order to prevent the folio
-getting mapped back in and redirtied, it needs to be kept locked
-across the entire operation.
-
 ->swap_activate() will be called to prepare the given file for swap.  It
 should perform any validation and preparation necessary to ensure that
 writes can be performed with minimal memory allocation.  It should call
diff --git a/Documentation/filesystems/vfs.rst b/Documentation/filesystems/vfs.rst
index 7c753148af88..bff64713bd09 100644
--- a/Documentation/filesystems/vfs.rst
+++ b/Documentation/filesystems/vfs.rst
@@ -768,7 +768,6 @@ cache in your filesystem.  The following members are defined:
 		ssize_t (*direct_IO)(struct kiocb *, struct iov_iter *iter);
 		int (*migrate_folio)(struct mapping *, struct folio *dst,
 				struct folio *src, enum migrate_mode);
-		int (*launder_folio) (struct folio *);
 
 		bool (*is_partially_uptodate) (struct folio *, size_t from,
 					       size_t count);
@@ -942,11 +941,6 @@ cache in your filesystem.  The following members are defined:
 	folio to this function.  migrate_folio should transfer any private
 	data across and update any references that it has to the folio.
 
-``launder_folio``
-	Called before freeing a folio - it writes back the dirty folio.
-	To prevent redirtying the folio, it is kept locked during the
-	whole operation.
-
 ``is_partially_uptodate``
 	Called by the VM when reading a file through the pagecache when
 	the underlying blocksize is smaller than the size of the folio.
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 50ce731a2b78..e0f4f518fe24 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -428,7 +428,6 @@ struct address_space_operations {
 	 */
 	int (*migrate_folio)(struct address_space *, struct folio *dst,
 			struct folio *src, enum migrate_mode);
-	int (*launder_folio)(struct folio *);
 	bool (*is_partially_uptodate) (struct folio *, size_t from,
 			size_t count);
 	void (*is_dirty_writeback) (struct folio *, bool *dirty, bool *wb);
diff --git a/mm/truncate.c b/mm/truncate.c
index b58ba940be47..7f7d65fa926a 100644
--- a/mm/truncate.c
+++ b/mm/truncate.c
@@ -603,15 +603,6 @@ unsigned long invalidate_mapping_pages(struct address_space *mapping,
 }
 EXPORT_SYMBOL(invalidate_mapping_pages);
 
-static int folio_launder(struct address_space *mapping, struct folio *folio)
-{
-	if (!folio_test_dirty(folio))
-		return 0;
-	if (folio->mapping != mapping || mapping->a_ops->launder_folio == NULL)
-		return 0;
-	return mapping->a_ops->launder_folio(folio);
-}
-
 /*
  * This is like mapping_evict_folio(), except it ignores the folio's
  * refcount.  We do this because invalidate_inode_pages2() needs stronger
@@ -623,7 +614,6 @@ int folio_unmap_invalidate(struct address_space *mapping, struct folio *folio,
 			   gfp_t gfp)
 {
 	void (*free_folio)(struct folio *);
-	int ret;
 
 	VM_BUG_ON_FOLIO(!folio_test_locked(folio), folio);
 
@@ -631,9 +621,8 @@ int folio_unmap_invalidate(struct address_space *mapping, struct folio *folio,
 		unmap_mapping_folio(folio);
 	BUG_ON(folio_mapped(folio));
 
-	ret = folio_launder(mapping, folio);
-	if (ret)
-		return ret;
+	if (folio_test_dirty(folio))
+		return -EBUSY;
 	if (folio->mapping != mapping)
 		return -EBUSY;
 	if (!filemap_release_folio(folio, gfp))
-- 
2.47.3
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.