From 972b9dd4e4180fbb2352bf2f0e015b7b63f5cca0 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 26 Mar 2026 10:54:16 +0100 Subject: fs: Ignore inode metadata buffers in inode_lru_isolate() 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 Link: https://patch.msgid.link/20260326095354.16340-64-jack@suse.cz Tested-by: syzbot@syzkaller.appspotmail.com Signed-off-by: Christian Brauner --- include/linux/buffer_head.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include/linux/buffer_head.h') diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index b16b88bfbc3e..631bf971efc0 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -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) {} -- cgit v1.2.3 From c86f5d25514c2a60fcf5ea0aa11c5d8bd1a313ef Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 26 Mar 2026 10:54:25 +0100 Subject: fs: Make bhs point to mapping_metadata_bhs Make buffer heads point to mapping_metadata_bhs instead of struct address_space. This makes the code more self contained. For the (only) case of IO error handling where we really need to reach struct address_space add a pointer to the mapping from mapping_metadata_bhs. Reviewed-by: Christoph Hellwig Signed-off-by: Jan Kara Link: https://patch.msgid.link/20260326095354.16340-73-jack@suse.cz Signed-off-by: Christian Brauner --- include/linux/buffer_head.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux/buffer_head.h') diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index 631bf971efc0..20636599d858 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -73,8 +73,8 @@ struct buffer_head { bh_end_io_t *b_end_io; /* I/O completion */ void *b_private; /* reserved for b_end_io */ struct list_head b_assoc_buffers; /* associated with another mapping */ - struct address_space *b_assoc_map; /* mapping this buffer is - associated with */ + struct mapping_metadata_bhs *b_mmb; /* head of the list of metadata bhs + * this buffer is associated with */ atomic_t b_count; /* users using this buffer_head */ spinlock_t b_uptodate_lock; /* Used by the first bh in a page, to * serialise IO completion of other -- cgit v1.2.3 From 025c9af1a20c8353f586c9bfd30705dfe4a277de Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 26 Mar 2026 10:54:26 +0100 Subject: fs: Switch inode_has_buffers() to take mapping_metadata_bhs As part of a move towards placing mapping_metadata_bhs in fs-private inode part, switch inode_has_buffers() to take mapping_metadata_bhs and rename the function to mmb_has_buffers(). Reviewed-by: Christoph Hellwig Signed-off-by: Jan Kara Link: https://patch.msgid.link/20260326095354.16340-74-jack@suse.cz Signed-off-by: Christian Brauner --- include/linux/buffer_head.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux/buffer_head.h') diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index 20636599d858..44094fd476f5 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -515,7 +515,7 @@ bool block_dirty_folio(struct address_space *mapping, struct folio *folio); void buffer_init(void); bool try_to_free_buffers(struct folio *folio); -int inode_has_buffers(struct inode *inode); +bool mmb_has_buffers(struct mapping_metadata_bhs *mmb); void invalidate_inode_buffers(struct inode *inode); int sync_mapping_buffers(struct address_space *mapping); void invalidate_bh_lrus(void); -- cgit v1.2.3 From a8c8122a3dac55d25a1912b8fec9b8cd7366c37a Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 26 Mar 2026 10:54:27 +0100 Subject: fs: Provide functions for handling mapping_metadata_bhs directly As part of transition toward moving mapping_metadata_bhs to fs-private part of the inode, provide functions for operations on this list directly instead of going through the inode / mapping. Signed-off-by: Jan Kara Link: https://patch.msgid.link/20260326095354.16340-75-jack@suse.cz Signed-off-by: Christian Brauner --- include/linux/buffer_head.h | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) (limited to 'include/linux/buffer_head.h') diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index 44094fd476f5..e207dcca7a25 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -205,12 +205,30 @@ struct buffer_head *create_empty_buffers(struct folio *folio, void end_buffer_read_sync(struct buffer_head *bh, int uptodate); void end_buffer_write_sync(struct buffer_head *bh, int uptodate); -/* Things to do with buffers at mapping->private_list */ -void mark_buffer_dirty_inode(struct buffer_head *bh, struct inode *inode); -int generic_buffers_fsync_noflush(struct file *file, loff_t start, loff_t end, - bool datasync); -int generic_buffers_fsync(struct file *file, loff_t start, loff_t end, - bool datasync); +/* Things to do with metadata buffers list */ +void mmb_mark_buffer_dirty(struct buffer_head *bh, struct mapping_metadata_bhs *mmb); +static inline void mark_buffer_dirty_inode(struct buffer_head *bh, + struct inode *inode) +{ + mmb_mark_buffer_dirty(bh, &inode->i_data.i_metadata_bhs); +} +int mmb_fsync_noflush(struct file *file, struct mapping_metadata_bhs *mmb, + loff_t start, loff_t end, bool datasync); +static inline int generic_buffers_fsync_noflush(struct file *file, + loff_t start, loff_t end, + bool datasync) +{ + return mmb_fsync_noflush(file, &file->f_mapping->i_metadata_bhs, + start, end, datasync); +} +int mmb_fsync(struct file *file, struct mapping_metadata_bhs *mmb, + loff_t start, loff_t end, bool datasync); +static inline int generic_buffers_fsync(struct file *file, + loff_t start, loff_t end, bool datasync) +{ + return mmb_fsync(file, &file->f_mapping->i_metadata_bhs, + start, end, datasync); +} void clean_bdev_aliases(struct block_device *bdev, sector_t block, sector_t len); static inline void clean_bdev_bh_alias(struct buffer_head *bh) @@ -515,9 +533,18 @@ bool block_dirty_folio(struct address_space *mapping, struct folio *folio); void buffer_init(void); bool try_to_free_buffers(struct folio *folio); +void mmb_init(struct mapping_metadata_bhs *mmb, struct address_space *mapping); bool mmb_has_buffers(struct mapping_metadata_bhs *mmb); -void invalidate_inode_buffers(struct inode *inode); -int sync_mapping_buffers(struct address_space *mapping); +void mmb_invalidate(struct mapping_metadata_bhs *mmb); +int mmb_sync(struct mapping_metadata_bhs *mmb); +static inline void invalidate_inode_buffers(struct inode *inode) +{ + mmb_invalidate(&inode->i_data.i_metadata_bhs); +} +static inline int sync_mapping_buffers(struct address_space *mapping) +{ + return mmb_sync(&mapping->i_metadata_bhs); +} void invalidate_bh_lrus(void); void invalidate_bh_lrus_cpu(void); bool has_bh_in_lru(int cpu, void *dummy); @@ -527,6 +554,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 mmb_sync(struct mapping_metadata_bhs *mmb) { return 0; } static inline void invalidate_inode_buffers(struct inode *inode) {} static inline int sync_mapping_buffers(struct address_space *mapping) { return 0; } static inline void invalidate_bh_lrus(void) {} -- cgit v1.2.3 From cb6d109b9ccc374d09812c2387ab826499ee6562 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 26 Mar 2026 10:54:35 +0100 Subject: fs: Drop mapping_metadata_bhs from address space Nobody uses mapping_metadata_bhs in struct address_space anymore. Just remove it and with it all helper functions using it. Signed-off-by: Jan Kara Link: https://patch.msgid.link/20260326095354.16340-83-jack@suse.cz Signed-off-by: Christian Brauner --- include/linux/buffer_head.h | 28 ---------------------------- 1 file changed, 28 deletions(-) (limited to 'include/linux/buffer_head.h') diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index e207dcca7a25..e4939e33b4b5 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -207,28 +207,10 @@ void end_buffer_write_sync(struct buffer_head *bh, int uptodate); /* Things to do with metadata buffers list */ void mmb_mark_buffer_dirty(struct buffer_head *bh, struct mapping_metadata_bhs *mmb); -static inline void mark_buffer_dirty_inode(struct buffer_head *bh, - struct inode *inode) -{ - mmb_mark_buffer_dirty(bh, &inode->i_data.i_metadata_bhs); -} int mmb_fsync_noflush(struct file *file, struct mapping_metadata_bhs *mmb, loff_t start, loff_t end, bool datasync); -static inline int generic_buffers_fsync_noflush(struct file *file, - loff_t start, loff_t end, - bool datasync) -{ - return mmb_fsync_noflush(file, &file->f_mapping->i_metadata_bhs, - start, end, datasync); -} int mmb_fsync(struct file *file, struct mapping_metadata_bhs *mmb, loff_t start, loff_t end, bool datasync); -static inline int generic_buffers_fsync(struct file *file, - loff_t start, loff_t end, bool datasync) -{ - return mmb_fsync(file, &file->f_mapping->i_metadata_bhs, - start, end, datasync); -} void clean_bdev_aliases(struct block_device *bdev, sector_t block, sector_t len); static inline void clean_bdev_bh_alias(struct buffer_head *bh) @@ -537,14 +519,6 @@ void mmb_init(struct mapping_metadata_bhs *mmb, struct address_space *mapping); bool mmb_has_buffers(struct mapping_metadata_bhs *mmb); void mmb_invalidate(struct mapping_metadata_bhs *mmb); int mmb_sync(struct mapping_metadata_bhs *mmb); -static inline void invalidate_inode_buffers(struct inode *inode) -{ - mmb_invalidate(&inode->i_data.i_metadata_bhs); -} -static inline int sync_mapping_buffers(struct address_space *mapping) -{ - return mmb_sync(&mapping->i_metadata_bhs); -} void invalidate_bh_lrus(void); void invalidate_bh_lrus_cpu(void); bool has_bh_in_lru(int cpu, void *dummy); @@ -555,8 +529,6 @@ 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 mmb_sync(struct mapping_metadata_bhs *mmb) { return 0; } -static inline void invalidate_inode_buffers(struct inode *inode) {} -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) {} static inline bool has_bh_in_lru(int cpu, void *dummy) { return false; } -- cgit v1.2.3