mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
fs: rotate inodes whose page cache could not be dropped
When inode_lru_isolate() drops the page cache of an unused inode, it has to unlock the list, so it returns LRU_RETRY and the walk restarts at the head of the list, where the inode still is. If invalidate_mapping_pages() could not empty the mapping, the restarted walk strips the same inode again, and so on until nr_to_walk runs out. Every walk of that list then ends on this inode, and nothing behind it is reclaimed for as long as its cache stays. Before commit2a0629834c("vfs: Don't evict inode under the inode lru traversing context") the walker pinned the inode with a reference, and dropping it in iput_final() set I_REFERENCED, so the restarted walk rotated the inode to the tail. Set I_REFERENCED again when the mapping is still populated after the invalidation, in the same i_lock section that clears I_LRU_ISOLATING. inode_unpin_lru_isolating() now expects i_lock to be held, like inode_pin_lru_isolating(). On CONFIG_HIGHMEM kernels this can happen to any unused inode: a folio is left behind if it is locked, under writeback, spliced into a pipe, or still in another CPU's LRU-add batch after being read. Without HIGHMEM it happens to DAX inodes whose only entry is at index 0, which mapping_shrinkable() lets onto the LRU and invalidate_mapping_pages() never removes. Fixes:2a0629834c("vfs: Don't evict inode under the inode lru traversing context") Cc: stable@vger.kernel.org Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com> Link: https://patch.msgid.link/20261016102515.111285-1-nick@karaolidis.com Reviewed-by: Jan Kara <jack@suse.cz> Reviewed-by: Zhihao Cheng <chengzhihao1@huawei.com> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
parent
46e66d41fa
commit
8ee09f5977
|
|
@ -593,12 +593,11 @@ static void inode_pin_lru_isolating(struct inode *inode)
|
|||
|
||||
static void inode_unpin_lru_isolating(struct inode *inode)
|
||||
{
|
||||
spin_lock(&inode->i_lock);
|
||||
lockdep_assert_held(&inode->i_lock);
|
||||
WARN_ON(!(inode_state_read(inode) & I_LRU_ISOLATING));
|
||||
inode_state_clear(inode, I_LRU_ISOLATING);
|
||||
/* Called with inode->i_lock which ensures memory ordering. */
|
||||
inode_wake_up_bit(inode, __I_LRU_ISOLATING);
|
||||
spin_unlock(&inode->i_lock);
|
||||
}
|
||||
|
||||
static void inode_wait_for_lru_isolating(struct inode *inode)
|
||||
|
|
@ -981,7 +980,13 @@ static enum lru_status inode_lru_isolate(struct list_head *item,
|
|||
else
|
||||
__count_vm_events(PGINODESTEAL, reap);
|
||||
mm_account_reclaimed_pages(reap);
|
||||
|
||||
spin_lock(&inode->i_lock);
|
||||
/* The walk restarts here: don't retry leftovers right away */
|
||||
if (!mapping_empty(&inode->i_data))
|
||||
inode_state_set(inode, I_REFERENCED);
|
||||
inode_unpin_lru_isolating(inode);
|
||||
spin_unlock(&inode->i_lock);
|
||||
return LRU_RETRY;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user