]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
fs: Ignore inode metadata buffers in inode_lru_isolate()
authorJan Kara <jack@suse.cz>
Thu, 26 Mar 2026 09:54:16 +0000 (10:54 +0100)
committerChristian Brauner <brauner@kernel.org>
Thu, 26 Mar 2026 14:03:29 +0000 (15:03 +0100)
There are only a few filesystems that use generic tracking of inode
metadata buffer heads. As such the logic to reclaim tracked metadata
buffer heads in inode_lru_isolate() doesn't bring a benefit big enough
to justify intertwining of inode reclaim and metadata buffer head
tracking. Just treat tracked metadata buffer heads as any other metadata
filesystem has to properly clean up on inode eviction and stop handling
it in inode_lru_isolate(). As a result filesystems using generic
tracking of metadata buffer heads may now see dirty metadata buffers in
their .evict methods more often which can slow down inode reclaim but
given these filesystems aren't used in performance demanding setups we
should be fine.

Signed-off-by: Jan Kara <jack@suse.cz>
Link: https://patch.msgid.link/20260326095354.16340-64-jack@suse.cz
Tested-by: syzbot@syzkaller.appspotmail.com
Signed-off-by: Christian Brauner <brauner@kernel.org>
fs/buffer.c
fs/inode.c
include/linux/buffer_head.h

index 1bc0f22f3cc25483aee9a1f728113197eaa3dae4..bd48644e1bf8d6f5d79ec2495c10bf34895a8e22 100644 (file)
@@ -878,35 +878,6 @@ void invalidate_inode_buffers(struct inode *inode)
 }
 EXPORT_SYMBOL(invalidate_inode_buffers);
 
-/*
- * Remove any clean buffers from the inode's buffer list.  This is called
- * when we're trying to free the inode itself.  Those buffers can pin it.
- *
- * Returns true if all buffers were removed.
- */
-int remove_inode_buffers(struct inode *inode)
-{
-       int ret = 1;
-
-       if (inode_has_buffers(inode)) {
-               struct address_space *mapping = &inode->i_data;
-               struct list_head *list = &mapping->i_private_list;
-               struct address_space *buffer_mapping = mapping->i_private_data;
-
-               spin_lock(&buffer_mapping->i_private_lock);
-               while (!list_empty(list)) {
-                       struct buffer_head *bh = BH_ENTRY(list->next);
-                       if (buffer_dirty(bh)) {
-                               ret = 0;
-                               break;
-                       }
-                       __remove_assoc_queue(bh);
-               }
-               spin_unlock(&buffer_mapping->i_private_lock);
-       }
-       return ret;
-}
-
 /*
  * Create the appropriate buffers when given a folio for data area and
  * the size of each buffer.. Use the bh->b_this_page linked list to
index cc12b68e021b2c97cc88a46ddc736334ecb8edfa..4f98a5f04bbda11834c55947c050ee207a2a4f92 100644 (file)
@@ -17,7 +17,6 @@
 #include <linux/fsverity.h>
 #include <linux/mount.h>
 #include <linux/posix_acl.h>
-#include <linux/buffer_head.h> /* for inode_has_buffers */
 #include <linux/ratelimit.h>
 #include <linux/list_lru.h>
 #include <linux/iversion.h>
@@ -367,7 +366,6 @@ struct inode *alloc_inode(struct super_block *sb)
 
 void __destroy_inode(struct inode *inode)
 {
-       BUG_ON(inode_has_buffers(inode));
        inode_detach_wb(inode);
        security_inode_free(inode);
        fsnotify_inode_delete(inode);
@@ -994,19 +992,18 @@ static enum lru_status inode_lru_isolate(struct list_head *item,
         * page cache in order to free up struct inodes: lowmem might
         * be under pressure before the cache inside the highmem zone.
         */
-       if (inode_has_buffers(inode) || !mapping_empty(&inode->i_data)) {
+       if (!mapping_empty(&inode->i_data)) {
+               unsigned long reap;
+
                inode_pin_lru_isolating(inode);
                spin_unlock(&inode->i_lock);
                spin_unlock(&lru->lock);
-               if (remove_inode_buffers(inode)) {
-                       unsigned long reap;
-                       reap = invalidate_mapping_pages(&inode->i_data, 0, -1);
-                       if (current_is_kswapd())
-                               __count_vm_events(KSWAPD_INODESTEAL, reap);
-                       else
-                               __count_vm_events(PGINODESTEAL, reap);
-                       mm_account_reclaimed_pages(reap);
-               }
+               reap = invalidate_mapping_pages(&inode->i_data, 0, -1);
+               if (current_is_kswapd())
+                       __count_vm_events(KSWAPD_INODESTEAL, reap);
+               else
+                       __count_vm_events(PGINODESTEAL, reap);
+               mm_account_reclaimed_pages(reap);
                inode_unpin_lru_isolating(inode);
                return LRU_RETRY;
        }
index b16b88bfbc3e70a2de4efce26ebba1fcf1916ad3..631bf971efc04e250434f0d793a1df75e8fbc483 100644 (file)
@@ -517,7 +517,6 @@ void buffer_init(void);
 bool try_to_free_buffers(struct folio *folio);
 int inode_has_buffers(struct inode *inode);
 void invalidate_inode_buffers(struct inode *inode);
-int remove_inode_buffers(struct inode *inode);
 int sync_mapping_buffers(struct address_space *mapping);
 void invalidate_bh_lrus(void);
 void invalidate_bh_lrus_cpu(void);
@@ -528,9 +527,7 @@ extern int buffer_heads_over_limit;
 
 static inline void buffer_init(void) {}
 static inline bool try_to_free_buffers(struct folio *folio) { return true; }
-static inline int inode_has_buffers(struct inode *inode) { return 0; }
 static inline void invalidate_inode_buffers(struct inode *inode) {}
-static inline int remove_inode_buffers(struct inode *inode) { return 1; }
 static inline int sync_mapping_buffers(struct address_space *mapping) { return 0; }
 static inline void invalidate_bh_lrus(void) {}
 static inline void invalidate_bh_lrus_cpu(void) {}