]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
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)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 27 Dec 2024 12:53:00 +0000 (13:53 +0100)
commit 6309b8ce98e9a18390b9fd8f03fc412f3c17aee9 upstream.

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>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/nilfs2/btnode.c
fs/nilfs2/gcinode.c
fs/nilfs2/inode.c
fs/nilfs2/nilfs.h

index 13d943df871dd08a5909ff8b9ff71b161c65974b..abe3d5e1d84cd1e2054266a626743baebd1cf895 100644 (file)
@@ -35,6 +35,7 @@ void nilfs_init_btnc_inode(struct inode *btnc_inode)
        ii->i_flags = 0;
        memset(&ii->i_bmap_data, 0, sizeof(struct nilfs_bmap));
        mapping_set_gfp_mask(btnc_inode->i_mapping, GFP_NOFS);
+       btnc_inode->i_mapping->a_ops = &nilfs_buffer_cache_aops;
 }
 
 void nilfs_btnode_cache_clear(struct address_space *btnc)
index 2f612288ea4517b829679e7446c599fdc2ff0ee4..8cdb1dc76f612e930044f7dedaa4d0a8b43ca3b8 100644 (file)
@@ -163,7 +163,7 @@ int nilfs_init_gcinode(struct inode *inode)
 
        inode->i_mode = S_IFREG;
        mapping_set_gfp_mask(inode->i_mapping, GFP_NOFS);
-       inode->i_mapping->a_ops = &empty_aops;
+       inode->i_mapping->a_ops = &nilfs_buffer_cache_aops;
 
        ii->i_flags = 0;
        nilfs_bmap_init_gc(ii->i_bmap);
index 8eb4288d46fe031f5b5c432634c9a869a263cd41..1e87fd68ef86aabfa5b2e3b27f0156b8f7385eb1 100644 (file)
@@ -309,6 +309,10 @@ const struct address_space_operations nilfs_aops = {
        .is_partially_uptodate  = block_is_partially_uptodate,
 };
 
+const struct address_space_operations nilfs_buffer_cache_aops = {
+       .invalidate_folio       = block_invalidate_folio,
+};
+
 static int nilfs_insert_inode_locked(struct inode *inode,
                                     struct nilfs_root *root,
                                     unsigned long ino)
@@ -748,6 +752,7 @@ struct inode *nilfs_iget_for_shadow(struct inode *inode)
        NILFS_I(s_inode)->i_flags = 0;
        memset(NILFS_I(s_inode)->i_bmap, 0, sizeof(struct nilfs_bmap));
        mapping_set_gfp_mask(s_inode->i_mapping, GFP_NOFS);
+       s_inode->i_mapping->a_ops = &nilfs_buffer_cache_aops;
 
        err = nilfs_attach_btree_node_cache(s_inode);
        if (unlikely(err)) {
index ee27bb370d776d09d6d96fcb7cb5c914ea66487d..5a880b4edf3dbdc34a288125d4ce1b2af2fafa05 100644 (file)
@@ -379,6 +379,7 @@ extern const struct file_operations nilfs_dir_operations;
 extern const struct inode_operations nilfs_file_inode_operations;
 extern const struct file_operations nilfs_file_operations;
 extern const struct address_space_operations nilfs_aops;
+extern const struct address_space_operations nilfs_buffer_cache_aops;
 extern const struct inode_operations nilfs_dir_inode_operations;
 extern const struct inode_operations nilfs_special_inode_operations;
 extern const struct inode_operations nilfs_symlink_inode_operations;