nilfs2: fix buffer head leaks in calls to truncate_inode_pages()
authorRyusuke Konishi <konishi.ryusuke@gmail.com>
Thu, 12 Dec 2024 16:43:28 +0000 (01:43 +0900)
committerAndrew Morton <akpm@linux-foundation.org>
Thu, 19 Dec 2024 03:04:45 +0000 (19:04 -0800)
commit6309b8ce98e9a18390b9fd8f03fc412f3c17aee9
tree744622f045408a04439df18dbe84be6aa1aa5857
parenta2e740e216f5bf49ccb83b6d490c72a340558a43
nilfs2: fix buffer head leaks in calls to truncate_inode_pages()

When block_invalidatepage was converted to block_invalidate_folio, the
fallback to block_invalidatepage in folio_invalidate() if the
address_space_operations method invalidatepage (currently
invalidate_folio) was not set, was removed.

Unfortunately, some pseudo-inodes in nilfs2 use empty_aops set by
inode_init_always_gfp() as is, or explicitly set it to
address_space_operations.  Therefore, with this change,
block_invalidatepage() is no longer called from folio_invalidate(), and as
a result, the buffer_head structures attached to these pages/folios are no
longer freed via try_to_free_buffers().

Thus, these buffer heads are now leaked by truncate_inode_pages(), which
cleans up the page cache from inode evict(), etc.

Three types of caches use empty_aops: gc inode caches and the DAT shadow
inode used by GC, and b-tree node caches.  Of these, b-tree node caches
explicitly call invalidate_mapping_pages() during cleanup, which involves
calling try_to_free_buffers(), so the leak was not visible during normal
operation but worsened when GC was performed.

Fix this issue by using address_space_operations with invalidate_folio set
to block_invalidate_folio instead of empty_aops, which will ensure the
same behavior as before.

Link: https://lkml.kernel.org/r/20241212164556.21338-1-konishi.ryusuke@gmail.com
Fixes: 7ba13abbd31e ("fs: Turn block_invalidatepage into block_invalidate_folio")
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Cc: <stable@vger.kernel.org> [5.18+]
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
fs/nilfs2/btnode.c
fs/nilfs2/gcinode.c
fs/nilfs2/inode.c
fs/nilfs2/nilfs.h