Commit 0201ebf2 authored by David Howells's avatar David Howells Committed by Andrew Morton
Browse files

mm: merge folio_has_private()/filemap_release_folio() call pairs

Patch series "mm, netfs, fscache: Stop read optimisation when folio
removed from pagecache", v7.

This fixes an optimisation in fscache whereby we don't read from the cache
for a particular file until we know that there's data there that we don't
have in the pagecache.  The problem is that I'm no longer using PG_fscache
(aka PG_private_2) to indicate that the page is cached and so I don't get
a notification when a cached page is dropped from the pagecache.

The first patch merges some folio_has_private() and
filemap_release_folio() pairs and introduces a helper,
folio_needs_release(), to indicate if a release is required.

The second patch is the actual fix.  Following Willy's suggestions[1], it
adds an AS_RELEASE_ALWAYS flag to an address_space that will make
filemap_release_folio() always call ->release_folio(), even if
PG_private/PG_private_2 aren't set.  folio_needs_release() is altered to
add a check for this.


This patch (of 2):

Make filemap_release_folio() check folio_has_private().  Then, in most
cases, where a call to folio_has_private() is immediately followed by a
call to filemap_release_folio(), we can get rid of the test in the pair.

There are a couple of sites in mm/vscan.c that this can't so easily be
done.  In shrink_folio_list(), there are actually three cases (something
different is done for incompletely invalidated buffers), but
filemap_release_folio() elides two of them.

In shrink_active_list(), we don't have have the folio lock yet, so the
check allows us to avoid locking the page unnecessarily.

A wrapper function to check if a folio needs release is provided for those
places that still need to do it in the mm/ directory.  This will acquire
additional parts to the condition in a future patch.

After this, the only remaining caller of folio_has_private() outside of
mm/ is a check in fuse.

Link: https://lkml.kernel.org/r/20230628104852.3391651-1-dhowells@redhat.com
Link: https://lkml.kernel.org/r/20230628104852.3391651-2-dhowells@redhat.com


Reported-by: default avatarRohith Surabattula <rohiths.msft@gmail.com>
Suggested-by: default avatarMatthew Wilcox <willy@infradead.org>
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Steve French <sfrench@samba.org>
Cc: Shyam Prasad N <nspmangalore@gmail.com>
Cc: Rohith Surabattula <rohiths.msft@gmail.com>
Cc: Dave Wysochanski <dwysocha@redhat.com>
Cc: Dominique Martinet <asmadeus@codewreck.org>
Cc: Ilya Dryomov <idryomov@gmail.com>
Cc: "Theodore Ts'o" <tytso@mit.edu>
Cc: Andreas Dilger <adilger.kernel@dilger.ca>
Cc: Xiubo Li <xiubli@redhat.com>
Cc: Jingbo Xu <jefflexu@linux.alibaba.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
parent dba438bd
Loading
Loading
Loading
Loading
+4 −8
Original line number Original line Diff line number Diff line
@@ -340,10 +340,8 @@ move_extent_per_page(struct file *o_filp, struct inode *donor_inode,
			ext4_double_up_write_data_sem(orig_inode, donor_inode);
			ext4_double_up_write_data_sem(orig_inode, donor_inode);
			goto data_copy;
			goto data_copy;
		}
		}
		if ((folio_has_private(folio[0]) &&
		if (!filemap_release_folio(folio[0], 0) ||
		     !filemap_release_folio(folio[0], 0)) ||
		    !filemap_release_folio(folio[1], 0)) {
		    (folio_has_private(folio[1]) &&
		     !filemap_release_folio(folio[1], 0))) {
			*err = -EBUSY;
			*err = -EBUSY;
			goto drop_data_sem;
			goto drop_data_sem;
		}
		}
@@ -362,10 +360,8 @@ move_extent_per_page(struct file *o_filp, struct inode *donor_inode,


	/* At this point all buffers in range are uptodate, old mapping layout
	/* At this point all buffers in range are uptodate, old mapping layout
	 * is no longer required, try to drop it now. */
	 * is no longer required, try to drop it now. */
	if ((folio_has_private(folio[0]) &&
	if (!filemap_release_folio(folio[0], 0) ||
		!filemap_release_folio(folio[0], 0)) ||
	    !filemap_release_folio(folio[1], 0)) {
	    (folio_has_private(folio[1]) &&
		!filemap_release_folio(folio[1], 0))) {
		*err = -EBUSY;
		*err = -EBUSY;
		goto unlock_folios;
		goto unlock_folios;
	}
	}
+1 −2
Original line number Original line Diff line number Diff line
@@ -83,8 +83,7 @@ static bool page_cache_pipe_buf_try_steal(struct pipe_inode_info *pipe,
		 */
		 */
		folio_wait_writeback(folio);
		folio_wait_writeback(folio);


		if (folio_has_private(folio) &&
		if (!filemap_release_folio(folio, GFP_KERNEL))
		    !filemap_release_folio(folio, GFP_KERNEL))
			goto out_unlock;
			goto out_unlock;


		/*
		/*
+2 −0
Original line number Original line Diff line number Diff line
@@ -4073,6 +4073,8 @@ bool filemap_release_folio(struct folio *folio, gfp_t gfp)
	struct address_space * const mapping = folio->mapping;
	struct address_space * const mapping = folio->mapping;


	BUG_ON(!folio_test_locked(folio));
	BUG_ON(!folio_test_locked(folio));
	if (!folio_needs_release(folio))
		return true;
	if (folio_test_writeback(folio))
	if (folio_test_writeback(folio))
		return false;
		return false;


+1 −2
Original line number Original line Diff line number Diff line
@@ -2697,8 +2697,7 @@ int split_huge_page_to_list(struct page *page, struct list_head *list)
		gfp = current_gfp_context(mapping_gfp_mask(mapping) &
		gfp = current_gfp_context(mapping_gfp_mask(mapping) &
							GFP_RECLAIM_MASK);
							GFP_RECLAIM_MASK);


		if (folio_test_private(folio) &&
		if (!filemap_release_folio(folio, gfp)) {
				!filemap_release_folio(folio, gfp)) {
			ret = -EBUSY;
			ret = -EBUSY;
			goto out;
			goto out;
		}
		}
+8 −0
Original line number Original line Diff line number Diff line
@@ -176,6 +176,14 @@ static inline void set_page_refcounted(struct page *page)
	set_page_count(page, 1);
	set_page_count(page, 1);
}
}


/*
 * Return true if a folio needs ->release_folio() calling upon it.
 */
static inline bool folio_needs_release(struct folio *folio)
{
	return folio_has_private(folio);
}

extern unsigned long highest_memmap_pfn;
extern unsigned long highest_memmap_pfn;


/*
/*
Loading