mm/vmscan: Convert pageout() to take a folio

We always write out an entire folio at once.  This conversion removes
a few calls to compound_head() and gets the NR_VMSCAN_WRITE statistic
right when writing out a large folio.

Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
This commit is contained in:
Matthew Wilcox (Oracle) 2022-01-17 23:35:57 -05:00
parent d92013d1e5
commit e0cd5e7ffa
2 changed files with 37 additions and 37 deletions

View File

@ -327,11 +327,11 @@ TRACE_EVENT(mm_vmscan_lru_isolate,
__print_symbolic(__entry->lru, LRU_NAMES))
);
TRACE_EVENT(mm_vmscan_writepage,
TRACE_EVENT(mm_vmscan_write_folio,
TP_PROTO(struct page *page),
TP_PROTO(struct folio *folio),
TP_ARGS(page),
TP_ARGS(folio),
TP_STRUCT__entry(
__field(unsigned long, pfn)
@ -339,9 +339,9 @@ TRACE_EVENT(mm_vmscan_writepage,
),
TP_fast_assign(
__entry->pfn = page_to_pfn(page);
__entry->pfn = folio_pfn(folio);
__entry->reclaim_flags = trace_reclaim_flags(
page_is_file_lru(page));
folio_is_file_lru(folio));
),
TP_printk("page=%p pfn=0x%lx flags=%s",

View File

@ -978,15 +978,15 @@ void drop_slab(void)
drop_slab_node(nid);
}
static inline int is_page_cache_freeable(struct page *page)
static inline int is_page_cache_freeable(struct folio *folio)
{
/*
* A freeable page cache page is referenced only by the caller
* that isolated the page, the page cache and optional buffer
* heads at page->private.
*/
int page_cache_pins = thp_nr_pages(page);
return page_count(page) - page_has_private(page) == 1 + page_cache_pins;
return folio_ref_count(folio) - folio_test_private(folio) ==
1 + folio_nr_pages(folio);
}
static int may_write_to_inode(struct inode *inode)
@ -1001,24 +1001,24 @@ static int may_write_to_inode(struct inode *inode)
}
/*
* We detected a synchronous write error writing a page out. Probably
* We detected a synchronous write error writing a folio out. Probably
* -ENOSPC. We need to propagate that into the address_space for a subsequent
* fsync(), msync() or close().
*
* The tricky part is that after writepage we cannot touch the mapping: nothing
* prevents it from being freed up. But we have a ref on the page and once
* that page is locked, the mapping is pinned.
* prevents it from being freed up. But we have a ref on the folio and once
* that folio is locked, the mapping is pinned.
*
* We're allowed to run sleeping lock_page() here because we know the caller has
* We're allowed to run sleeping folio_lock() here because we know the caller has
* __GFP_FS.
*/
static void handle_write_error(struct address_space *mapping,
struct page *page, int error)
struct folio *folio, int error)
{
lock_page(page);
if (page_mapping(page) == mapping)
folio_lock(folio);
if (folio_mapping(folio) == mapping)
mapping_set_error(mapping, error);
unlock_page(page);
folio_unlock(folio);
}
static bool skip_throttle_noprogress(pg_data_t *pgdat)
@ -1165,35 +1165,35 @@ typedef enum {
* pageout is called by shrink_page_list() for each dirty page.
* Calls ->writepage().
*/
static pageout_t pageout(struct page *page, struct address_space *mapping)
static pageout_t pageout(struct folio *folio, struct address_space *mapping)
{
/*
* If the page is dirty, only perform writeback if that write
* If the folio is dirty, only perform writeback if that write
* will be non-blocking. To prevent this allocation from being
* stalled by pagecache activity. But note that there may be
* stalls if we need to run get_block(). We could test
* PagePrivate for that.
*
* If this process is currently in __generic_file_write_iter() against
* this page's queue, we can perform writeback even if that
* this folio's queue, we can perform writeback even if that
* will block.
*
* If the page is swapcache, write it back even if that would
* If the folio is swapcache, write it back even if that would
* block, for some throttling. This happens by accident, because
* swap_backing_dev_info is bust: it doesn't reflect the
* congestion state of the swapdevs. Easy to fix, if needed.
*/
if (!is_page_cache_freeable(page))
if (!is_page_cache_freeable(folio))
return PAGE_KEEP;
if (!mapping) {
/*
* Some data journaling orphaned pages can have
* page->mapping == NULL while being dirty with clean buffers.
* Some data journaling orphaned folios can have
* folio->mapping == NULL while being dirty with clean buffers.
*/
if (page_has_private(page)) {
if (try_to_free_buffers(page)) {
ClearPageDirty(page);
pr_info("%s: orphaned page\n", __func__);
if (folio_test_private(folio)) {
if (try_to_free_buffers(&folio->page)) {
folio_clear_dirty(folio);
pr_info("%s: orphaned folio\n", __func__);
return PAGE_CLEAN;
}
}
@ -1204,7 +1204,7 @@ static pageout_t pageout(struct page *page, struct address_space *mapping)
if (!may_write_to_inode(mapping->host))
return PAGE_KEEP;
if (clear_page_dirty_for_io(page)) {
if (folio_clear_dirty_for_io(folio)) {
int res;
struct writeback_control wbc = {
.sync_mode = WB_SYNC_NONE,
@ -1214,21 +1214,21 @@ static pageout_t pageout(struct page *page, struct address_space *mapping)
.for_reclaim = 1,
};
SetPageReclaim(page);
res = mapping->a_ops->writepage(page, &wbc);
folio_set_reclaim(folio);
res = mapping->a_ops->writepage(&folio->page, &wbc);
if (res < 0)
handle_write_error(mapping, page, res);
handle_write_error(mapping, folio, res);
if (res == AOP_WRITEPAGE_ACTIVATE) {
ClearPageReclaim(page);
folio_clear_reclaim(folio);
return PAGE_ACTIVATE;
}
if (!PageWriteback(page)) {
if (!folio_test_writeback(folio)) {
/* synchronous write or broken a_ops? */
ClearPageReclaim(page);
folio_clear_reclaim(folio);
}
trace_mm_vmscan_writepage(page);
inc_node_page_state(page, NR_VMSCAN_WRITE);
trace_mm_vmscan_write_folio(folio);
node_stat_add_folio(folio, NR_VMSCAN_WRITE);
return PAGE_SUCCESS;
}
@ -1816,7 +1816,7 @@ static unsigned int shrink_page_list(struct list_head *page_list,
* starts and then write it out here.
*/
try_to_unmap_flush_dirty();
switch (pageout(page, mapping)) {
switch (pageout(folio, mapping)) {
case PAGE_KEEP:
goto keep_locked;
case PAGE_ACTIVATE: