Skip to content

Commit a8c8122

Browse files
jankarabrauner
authored andcommitted
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 <jack@suse.cz> Link: https://patch.msgid.link/20260326095354.16340-75-jack@suse.cz Signed-off-by: Christian Brauner <brauner@kernel.org>
1 parent 025c9af commit a8c8122

2 files changed

Lines changed: 87 additions & 67 deletions

File tree

fs/buffer.c

Lines changed: 51 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -467,31 +467,25 @@ EXPORT_SYMBOL(mark_buffer_async_write);
467467
* a successful fsync(). For example, ext2 indirect blocks need to be
468468
* written back and waited upon before fsync() returns.
469469
*
470-
* The functions mark_buffer_dirty_inode(), fsync_inode_buffers(),
471-
* mmb_has_buffers() and invalidate_inode_buffers() are provided for the
472-
* management of a list of dependent buffers in mapping_metadata_bhs struct.
470+
* The functions mmb_mark_buffer_dirty(), mmb_sync(), mmb_has_buffers()
471+
* and mmb_invalidate() are provided for the management of a list of dependent
472+
* buffers in mapping_metadata_bhs struct.
473473
*
474474
* The locking is a little subtle: The list of buffer heads is protected by
475475
* the lock in mapping_metadata_bhs so functions coming from bdev mapping
476476
* (such as try_to_free_buffers()) need to safely get to mapping_metadata_bhs
477477
* using RCU, grab the lock, verify we didn't race with somebody detaching the
478478
* bh / moving it to different inode and only then proceeding.
479-
*
480-
* FIXME: mark_buffer_dirty_inode() is a data-plane operation. It should
481-
* take an address_space, not an inode. And it should be called
482-
* mark_buffer_dirty_fsync() to clearly define why those buffers are being
483-
* queued up.
484-
*
485-
* FIXME: mark_buffer_dirty_inode() doesn't need to add the buffer to the
486-
* list if it is already on a list. Because if the buffer is on a list,
487-
* it *must* already be on the right one. If not, the filesystem is being
488-
* silly. This will save a ton of locking. But first we have to ensure
489-
* that buffers are taken *off* the old inode's list when they are freed
490-
* (presumably in truncate). That requires careful auditing of all
491-
* filesystems (do it inside bforget()). It could also be done by bringing
492-
* b_inode back.
493479
*/
494480

481+
void mmb_init(struct mapping_metadata_bhs *mmb, struct address_space *mapping)
482+
{
483+
spin_lock_init(&mmb->lock);
484+
INIT_LIST_HEAD(&mmb->list);
485+
mmb->mapping = mapping;
486+
}
487+
EXPORT_SYMBOL(mmb_init);
488+
495489
static void __remove_assoc_queue(struct mapping_metadata_bhs *mmb,
496490
struct buffer_head *bh)
497491
{
@@ -533,12 +527,12 @@ bool mmb_has_buffers(struct mapping_metadata_bhs *mmb)
533527
EXPORT_SYMBOL_GPL(mmb_has_buffers);
534528

535529
/**
536-
* sync_mapping_buffers - write out & wait upon a mapping's "associated" buffers
537-
* @mapping: the mapping which wants those buffers written
530+
* mmb_sync - write out & wait upon all buffers in a list
531+
* @mmb: the list of buffers to write
538532
*
539-
* Starts I/O against the buffers at mapping->i_metadata_bhs and waits upon
540-
* that I/O. Basically, this is a convenience function for fsync(). @mapping
541-
* is a file or directory which needs those buffers to be written for a
533+
* Starts I/O against the buffers in the given list and waits upon
534+
* that I/O. Basically, this is a convenience function for fsync(). @mmb is
535+
* for a file or directory which needs those buffers to be written for a
542536
* successful fsync().
543537
*
544538
* We have conflicting pressures: we want to make sure that all
@@ -553,9 +547,8 @@ EXPORT_SYMBOL_GPL(mmb_has_buffers);
553547
* buffer stays on our list until IO completes (at which point it can be
554548
* reaped).
555549
*/
556-
int sync_mapping_buffers(struct address_space *mapping)
550+
int mmb_sync(struct mapping_metadata_bhs *mmb)
557551
{
558-
struct mapping_metadata_bhs *mmb = &mapping->i_metadata_bhs;
559552
struct buffer_head *bh;
560553
int err = 0;
561554
struct blk_plug plug;
@@ -626,33 +619,35 @@ int sync_mapping_buffers(struct address_space *mapping)
626619
spin_unlock(&mmb->lock);
627620
return err;
628621
}
629-
EXPORT_SYMBOL(sync_mapping_buffers);
622+
EXPORT_SYMBOL(mmb_sync);
630623

631624
/**
632-
* generic_buffers_fsync_noflush - generic buffer fsync implementation
633-
* for simple filesystems with no inode lock
625+
* mmb_fsync_noflush - fsync implementation for simple filesystems with
626+
* metadata buffers list
634627
*
635628
* @file: file to synchronize
629+
* @mmb: list of metadata bhs to flush
636630
* @start: start offset in bytes
637631
* @end: end offset in bytes (inclusive)
638632
* @datasync: only synchronize essential metadata if true
639633
*
640-
* This is a generic implementation of the fsync method for simple
641-
* filesystems which track all non-inode metadata in the buffers list
642-
* hanging off the address_space structure.
634+
* This is an implementation of the fsync method for simple filesystems which
635+
* track all non-inode metadata in the buffers list hanging off the @mmb
636+
* structure.
643637
*/
644-
int generic_buffers_fsync_noflush(struct file *file, loff_t start, loff_t end,
645-
bool datasync)
638+
int mmb_fsync_noflush(struct file *file, struct mapping_metadata_bhs *mmb,
639+
loff_t start, loff_t end, bool datasync)
646640
{
647641
struct inode *inode = file->f_mapping->host;
648642
int err;
649-
int ret;
643+
int ret = 0;
650644

651645
err = file_write_and_wait_range(file, start, end);
652646
if (err)
653647
return err;
654648

655-
ret = sync_mapping_buffers(inode->i_mapping);
649+
if (mmb)
650+
ret = mmb_sync(mmb);
656651
if (!(inode_state_read_once(inode) & I_DIRTY_ALL))
657652
goto out;
658653
if (datasync && !(inode_state_read_once(inode) & I_DIRTY_DATASYNC))
@@ -669,34 +664,35 @@ int generic_buffers_fsync_noflush(struct file *file, loff_t start, loff_t end,
669664
ret = err;
670665
return ret;
671666
}
672-
EXPORT_SYMBOL(generic_buffers_fsync_noflush);
667+
EXPORT_SYMBOL(mmb_fsync_noflush);
673668

674669
/**
675-
* generic_buffers_fsync - generic buffer fsync implementation
676-
* for simple filesystems with no inode lock
670+
* mmb_fsync - fsync implementation for simple filesystems with metadata
671+
* buffers list
677672
*
678673
* @file: file to synchronize
674+
* @mmb: list of metadata bhs to flush
679675
* @start: start offset in bytes
680676
* @end: end offset in bytes (inclusive)
681677
* @datasync: only synchronize essential metadata if true
682678
*
683-
* This is a generic implementation of the fsync method for simple
684-
* filesystems which track all non-inode metadata in the buffers list
685-
* hanging off the address_space structure. This also makes sure that
686-
* a device cache flush operation is called at the end.
679+
* This is an implementation of the fsync method for simple filesystems which
680+
* track all non-inode metadata in the buffers list hanging off the @mmb
681+
* structure. This also makes sure that a device cache flush operation is
682+
* called at the end.
687683
*/
688-
int generic_buffers_fsync(struct file *file, loff_t start, loff_t end,
689-
bool datasync)
684+
int mmb_fsync(struct file *file, struct mapping_metadata_bhs *mmb,
685+
loff_t start, loff_t end, bool datasync)
690686
{
691687
struct inode *inode = file->f_mapping->host;
692688
int ret;
693689

694-
ret = generic_buffers_fsync_noflush(file, start, end, datasync);
690+
ret = mmb_fsync_noflush(file, mmb, start, end, datasync);
695691
if (!ret)
696692
ret = blkdev_issue_flush(inode->i_sb->s_bdev);
697693
return ret;
698694
}
699-
EXPORT_SYMBOL(generic_buffers_fsync);
695+
EXPORT_SYMBOL(mmb_fsync);
700696

701697
/*
702698
* Called when we've recently written block `bblock', and it is known that
@@ -717,20 +713,18 @@ void write_boundary_block(struct block_device *bdev,
717713
}
718714
}
719715

720-
void mark_buffer_dirty_inode(struct buffer_head *bh, struct inode *inode)
716+
void mmb_mark_buffer_dirty(struct buffer_head *bh,
717+
struct mapping_metadata_bhs *mmb)
721718
{
722-
struct address_space *mapping = inode->i_mapping;
723-
724719
mark_buffer_dirty(bh);
725720
if (!bh->b_mmb) {
726-
spin_lock(&mapping->i_metadata_bhs.lock);
727-
list_move_tail(&bh->b_assoc_buffers,
728-
&mapping->i_metadata_bhs.list);
729-
bh->b_mmb = &mapping->i_metadata_bhs;
730-
spin_unlock(&mapping->i_metadata_bhs.lock);
721+
spin_lock(&mmb->lock);
722+
list_move_tail(&bh->b_assoc_buffers, &mmb->list);
723+
bh->b_mmb = mmb;
724+
spin_unlock(&mmb->lock);
731725
}
732726
}
733-
EXPORT_SYMBOL(mark_buffer_dirty_inode);
727+
EXPORT_SYMBOL(mmb_mark_buffer_dirty);
734728

735729
/**
736730
* block_dirty_folio - Mark a folio as dirty.
@@ -797,22 +791,20 @@ bool block_dirty_folio(struct address_space *mapping, struct folio *folio)
797791
EXPORT_SYMBOL(block_dirty_folio);
798792

799793
/*
800-
* Invalidate any and all dirty buffers on a given inode. We are
794+
* Invalidate any and all dirty buffers on a given buffers list. We are
801795
* probably unmounting the fs, but that doesn't mean we have already
802796
* done a sync(). Just drop the buffers from the inode list.
803797
*/
804-
void invalidate_inode_buffers(struct inode *inode)
798+
void mmb_invalidate(struct mapping_metadata_bhs *mmb)
805799
{
806-
struct mapping_metadata_bhs *mmb = &inode->i_data.i_metadata_bhs;
807-
808800
if (mmb_has_buffers(mmb)) {
809801
spin_lock(&mmb->lock);
810802
while (!list_empty(&mmb->list))
811803
__remove_assoc_queue(mmb, BH_ENTRY(mmb->list.next));
812804
spin_unlock(&mmb->lock);
813805
}
814806
}
815-
EXPORT_SYMBOL(invalidate_inode_buffers);
807+
EXPORT_SYMBOL(mmb_invalidate);
816808

817809
/*
818810
* Create the appropriate buffers when given a folio for data area and

include/linux/buffer_head.h

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -205,12 +205,30 @@ struct buffer_head *create_empty_buffers(struct folio *folio,
205205
void end_buffer_read_sync(struct buffer_head *bh, int uptodate);
206206
void end_buffer_write_sync(struct buffer_head *bh, int uptodate);
207207

208-
/* Things to do with buffers at mapping->private_list */
209-
void mark_buffer_dirty_inode(struct buffer_head *bh, struct inode *inode);
210-
int generic_buffers_fsync_noflush(struct file *file, loff_t start, loff_t end,
211-
bool datasync);
212-
int generic_buffers_fsync(struct file *file, loff_t start, loff_t end,
213-
bool datasync);
208+
/* Things to do with metadata buffers list */
209+
void mmb_mark_buffer_dirty(struct buffer_head *bh, struct mapping_metadata_bhs *mmb);
210+
static inline void mark_buffer_dirty_inode(struct buffer_head *bh,
211+
struct inode *inode)
212+
{
213+
mmb_mark_buffer_dirty(bh, &inode->i_data.i_metadata_bhs);
214+
}
215+
int mmb_fsync_noflush(struct file *file, struct mapping_metadata_bhs *mmb,
216+
loff_t start, loff_t end, bool datasync);
217+
static inline int generic_buffers_fsync_noflush(struct file *file,
218+
loff_t start, loff_t end,
219+
bool datasync)
220+
{
221+
return mmb_fsync_noflush(file, &file->f_mapping->i_metadata_bhs,
222+
start, end, datasync);
223+
}
224+
int mmb_fsync(struct file *file, struct mapping_metadata_bhs *mmb,
225+
loff_t start, loff_t end, bool datasync);
226+
static inline int generic_buffers_fsync(struct file *file,
227+
loff_t start, loff_t end, bool datasync)
228+
{
229+
return mmb_fsync(file, &file->f_mapping->i_metadata_bhs,
230+
start, end, datasync);
231+
}
214232
void clean_bdev_aliases(struct block_device *bdev, sector_t block,
215233
sector_t len);
216234
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);
515533

516534
void buffer_init(void);
517535
bool try_to_free_buffers(struct folio *folio);
536+
void mmb_init(struct mapping_metadata_bhs *mmb, struct address_space *mapping);
518537
bool mmb_has_buffers(struct mapping_metadata_bhs *mmb);
519-
void invalidate_inode_buffers(struct inode *inode);
520-
int sync_mapping_buffers(struct address_space *mapping);
538+
void mmb_invalidate(struct mapping_metadata_bhs *mmb);
539+
int mmb_sync(struct mapping_metadata_bhs *mmb);
540+
static inline void invalidate_inode_buffers(struct inode *inode)
541+
{
542+
mmb_invalidate(&inode->i_data.i_metadata_bhs);
543+
}
544+
static inline int sync_mapping_buffers(struct address_space *mapping)
545+
{
546+
return mmb_sync(&mapping->i_metadata_bhs);
547+
}
521548
void invalidate_bh_lrus(void);
522549
void invalidate_bh_lrus_cpu(void);
523550
bool has_bh_in_lru(int cpu, void *dummy);
@@ -527,6 +554,7 @@ extern int buffer_heads_over_limit;
527554

528555
static inline void buffer_init(void) {}
529556
static inline bool try_to_free_buffers(struct folio *folio) { return true; }
557+
static inline int mmb_sync(struct mapping_metadata_bhs *mmb) { return 0; }
530558
static inline void invalidate_inode_buffers(struct inode *inode) {}
531559
static inline int sync_mapping_buffers(struct address_space *mapping) { return 0; }
532560
static inline void invalidate_bh_lrus(void) {}

0 commit comments

Comments
 (0)