* [PATCH 29/41] fs: Make bhs point to mapping_metadata_bhs
From: Jan Kara @ 2026-03-20 13:41 UTC (permalink / raw)
To: linux-fsdevel
Cc: linux-block, Christian Brauner, Al Viro, linux-ext4, Ted Tso,
Tigran A. Aivazian, David Sterba, OGAWA Hirofumi, Muchun Song,
Oscar Salvador, David Hildenbrand, linux-mm, linux-aio,
Benjamin LaHaise, Jan Kara
In-Reply-To: <20260320131728.6449-1-jack@suse.cz>
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.
Signed-off-by: Jan Kara <jack@suse.cz>
---
fs/buffer.c | 34 ++++++++++++++++------------------
fs/inode.c | 1 +
include/linux/buffer_head.h | 4 ++--
include/linux/fs.h | 1 +
4 files changed, 20 insertions(+), 20 deletions(-)
diff --git a/fs/buffer.c b/fs/buffer.c
index d39ae6581c26..e0e522b0cdad 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -497,13 +497,12 @@ static void __remove_assoc_queue(struct mapping_metadata_bhs *mmb,
{
lockdep_assert_held(&mmb->lock);
list_del_init(&bh->b_assoc_buffers);
- WARN_ON(!bh->b_assoc_map);
- bh->b_assoc_map = NULL;
+ WARN_ON(!bh->b_mmb);
+ bh->b_mmb = NULL;
}
static void remove_assoc_queue(struct buffer_head *bh)
{
- struct address_space *mapping;
struct mapping_metadata_bhs *mmb;
/*
@@ -514,13 +513,12 @@ static void remove_assoc_queue(struct buffer_head *bh)
* opportunistically acquire the lock and then recheck the bh
* didn't move under us.
*/
- while (bh->b_assoc_map) {
+ while (bh->b_mmb) {
rcu_read_lock();
- mapping = READ_ONCE(bh->b_assoc_map);
- if (mapping) {
- mmb = &mapping->i_metadata_bhs;
+ mmb = READ_ONCE(bh->b_mmb);
+ if (mmb) {
spin_lock(&mmb->lock);
- if (bh->b_assoc_map == mapping)
+ if (bh->b_mmb == mmb)
__remove_assoc_queue(mmb, bh);
spin_unlock(&mmb->lock);
}
@@ -551,9 +549,9 @@ EXPORT_SYMBOL_GPL(inode_has_buffers);
* Do this in two main stages: first we copy dirty buffers to a
* temporary inode list, queueing the writes as we go. Then we clean
* up, waiting for those writes to complete. mark_buffer_dirty_inode()
- * doesn't touch b_assoc_buffers list if b_assoc_map is not NULL so we
- * are sure the buffer stays on our list until IO completes (at which point
- * it can be reaped).
+ * doesn't touch b_assoc_buffers list if b_mmb is not NULL so we are sure the
+ * buffer stays on our list until IO completes (at which point it can be
+ * reaped).
*/
int sync_mapping_buffers(struct address_space *mapping)
{
@@ -571,14 +569,14 @@ int sync_mapping_buffers(struct address_space *mapping)
spin_lock(&mmb->lock);
while (!list_empty(&mmb->list)) {
bh = BH_ENTRY(mmb->list.next);
- WARN_ON_ONCE(bh->b_assoc_map != mapping);
+ WARN_ON_ONCE(bh->b_mmb != mmb);
__remove_assoc_queue(mmb, bh);
/* Avoid race with mark_buffer_dirty_inode() which does
* a lockless check and we rely on seeing the dirty bit */
smp_mb();
if (buffer_dirty(bh) || buffer_locked(bh)) {
list_add(&bh->b_assoc_buffers, &tmp);
- bh->b_assoc_map = mapping;
+ bh->b_mmb = mmb;
if (buffer_dirty(bh)) {
get_bh(bh);
spin_unlock(&mmb->lock);
@@ -616,7 +614,7 @@ int sync_mapping_buffers(struct address_space *mapping)
smp_mb();
if (buffer_dirty(bh)) {
list_add(&bh->b_assoc_buffers, &mmb->list);
- bh->b_assoc_map = mapping;
+ bh->b_mmb = mmb;
}
spin_unlock(&mmb->lock);
wait_on_buffer(bh);
@@ -724,11 +722,11 @@ void mark_buffer_dirty_inode(struct buffer_head *bh, struct inode *inode)
struct address_space *mapping = inode->i_mapping;
mark_buffer_dirty(bh);
- if (!bh->b_assoc_map) {
+ if (!bh->b_mmb) {
spin_lock(&mapping->i_metadata_bhs.lock);
list_move_tail(&bh->b_assoc_buffers,
&mapping->i_metadata_bhs.list);
- bh->b_assoc_map = mapping;
+ bh->b_mmb = &mapping->i_metadata_bhs;
spin_unlock(&mapping->i_metadata_bhs.lock);
}
}
@@ -1124,8 +1122,8 @@ void mark_buffer_write_io_error(struct buffer_head *bh)
/* FIXME: do we need to set this in both places? */
if (bh->b_folio && bh->b_folio->mapping)
mapping_set_error(bh->b_folio->mapping, -EIO);
- if (bh->b_assoc_map)
- mapping_set_error(bh->b_assoc_map, -EIO);
+ if (bh->b_mmb)
+ mapping_set_error(bh->b_mmb->mapping, -EIO);
}
EXPORT_SYMBOL(mark_buffer_write_io_error);
diff --git a/fs/inode.c b/fs/inode.c
index 393f586d050a..3874b933abdb 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -276,6 +276,7 @@ int inode_init_always_gfp(struct super_block *sb, struct inode *inode, gfp_t gfp
mapping->a_ops = &empty_aops;
mapping->host = inode;
+ mapping->i_metadata_bhs.mapping = mapping;
mapping->flags = 0;
mapping->wb_err = 0;
atomic_set(&mapping->i_mmap_writable, 0);
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
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 64771a55adc5..c4ab53ec36ab 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -447,6 +447,7 @@ extern const struct address_space_operations empty_aops;
/* Structure for tracking metadata buffer heads associated with the mapping */
struct mapping_metadata_bhs {
+ struct address_space *mapping; /* Mapping bhs are associated with */
spinlock_t lock; /* Lock protecting bh list */
struct list_head list; /* The list of bhs (b_assoc_buffers) */
};
--
2.51.0
^ permalink raw reply related
* [PATCH 31/41] fs: Provide functions for handling mapping_metadata_bhs directly
From: Jan Kara @ 2026-03-20 13:41 UTC (permalink / raw)
To: linux-fsdevel
Cc: linux-block, Christian Brauner, Al Viro, linux-ext4, Ted Tso,
Tigran A. Aivazian, David Sterba, OGAWA Hirofumi, Muchun Song,
Oscar Salvador, David Hildenbrand, linux-mm, linux-aio,
Benjamin LaHaise, Jan Kara
In-Reply-To: <20260320131728.6449-1-jack@suse.cz>
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>
---
fs/buffer.c | 93 +++++++++++++++++--------------------
include/linux/buffer_head.h | 45 ++++++++++++++----
2 files changed, 80 insertions(+), 58 deletions(-)
diff --git a/fs/buffer.c b/fs/buffer.c
index c70f8027bdd1..43aca5b7969f 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -467,31 +467,25 @@ EXPORT_SYMBOL(mark_buffer_async_write);
* a successful fsync(). For example, ext2 indirect blocks need to be
* written back and waited upon before fsync() returns.
*
- * The functions mark_buffer_dirty_inode(), fsync_inode_buffers(),
- * mmb_has_buffers() and invalidate_inode_buffers() are provided for the
- * management of a list of dependent buffers in mapping_metadata_bhs struct.
+ * The functions mmb_mark_buffer_dirty(), mmb_sync_buffers(), mmb_has_buffers()
+ * and mmb_invalidate_buffers() are provided for the management of a list of
+ * dependent buffers in mapping_metadata_bhs struct.
*
* The locking is a little subtle: The list of buffer heads is protected by
* the lock in mapping_metadata_bhs so functions coming from bdev mapping
* (such as try_to_free_buffers()) need to safely get to mapping_metadata_bhs
* using RCU, grab the lock, verify we didn't race with somebody detaching the
* bh / moving it to different inode and only then proceeding.
- *
- * FIXME: mark_buffer_dirty_inode() is a data-plane operation. It should
- * take an address_space, not an inode. And it should be called
- * mark_buffer_dirty_fsync() to clearly define why those buffers are being
- * queued up.
- *
- * FIXME: mark_buffer_dirty_inode() doesn't need to add the buffer to the
- * list if it is already on a list. Because if the buffer is on a list,
- * it *must* already be on the right one. If not, the filesystem is being
- * silly. This will save a ton of locking. But first we have to ensure
- * that buffers are taken *off* the old inode's list when they are freed
- * (presumably in truncate). That requires careful auditing of all
- * filesystems (do it inside bforget()). It could also be done by bringing
- * b_inode back.
*/
+void mmb_init(struct mapping_metadata_bhs *mmb, struct address_space *mapping)
+{
+ spin_lock_init(&mmb->lock);
+ INIT_LIST_HEAD(&mmb->list);
+ mmb->mapping = mapping;
+}
+EXPORT_SYMBOL(mmb_init);
+
static void __remove_assoc_queue(struct mapping_metadata_bhs *mmb,
struct buffer_head *bh)
{
@@ -533,12 +527,12 @@ bool mmb_has_buffers(struct mapping_metadata_bhs *mmb)
EXPORT_SYMBOL_GPL(mmb_has_buffers);
/**
- * sync_mapping_buffers - write out & wait upon a mapping's "associated" buffers
- * @mapping: the mapping which wants those buffers written
+ * mmb_sync_buffers - write out & wait upon all buffers in a list
+ * @mmb: the list of buffers to write
*
- * Starts I/O against the buffers at mapping->i_metadata_bhs and waits upon
- * that I/O. Basically, this is a convenience function for fsync(). @mapping
- * is a file or directory which needs those buffers to be written for a
+ * Starts I/O against the buffers in the given list and waits upon
+ * that I/O. Basically, this is a convenience function for fsync(). @mmb is
+ * for a file or directory which needs those buffers to be written for a
* successful fsync().
*
* We have conflicting pressures: we want to make sure that all
@@ -553,9 +547,8 @@ EXPORT_SYMBOL_GPL(mmb_has_buffers);
* buffer stays on our list until IO completes (at which point it can be
* reaped).
*/
-int sync_mapping_buffers(struct address_space *mapping)
+int mmb_sync_buffers(struct mapping_metadata_bhs *mmb)
{
- struct mapping_metadata_bhs *mmb = &mapping->i_metadata_bhs;
struct buffer_head *bh;
int err = 0;
struct blk_plug plug;
@@ -626,13 +619,14 @@ int sync_mapping_buffers(struct address_space *mapping)
spin_unlock(&mmb->lock);
return err;
}
-EXPORT_SYMBOL(sync_mapping_buffers);
+EXPORT_SYMBOL(mmb_sync_buffers);
/**
- * generic_buffers_fsync_noflush - generic buffer fsync implementation
+ * generic_mmb_fsync_noflush - generic buffer fsync implementation
* for simple filesystems with no inode lock
*
* @file: file to synchronize
+ * @mmb: list of metadata bhs to flush
* @start: start offset in bytes
* @end: end offset in bytes (inclusive)
* @datasync: only synchronize essential metadata if true
@@ -641,18 +635,20 @@ EXPORT_SYMBOL(sync_mapping_buffers);
* filesystems which track all non-inode metadata in the buffers list
* hanging off the address_space structure.
*/
-int generic_buffers_fsync_noflush(struct file *file, loff_t start, loff_t end,
- bool datasync)
+int generic_mmb_fsync_noflush(struct file *file,
+ struct mapping_metadata_bhs *mmb,
+ loff_t start, loff_t end, bool datasync)
{
struct inode *inode = file->f_mapping->host;
int err;
- int ret;
+ int ret = 0;
err = file_write_and_wait_range(file, start, end);
if (err)
return err;
- ret = sync_mapping_buffers(inode->i_mapping);
+ if (mmb)
+ ret = mmb_sync_buffers(mmb);
if (!(inode_state_read_once(inode) & I_DIRTY_ALL))
goto out;
if (datasync && !(inode_state_read_once(inode) & I_DIRTY_DATASYNC))
@@ -669,13 +665,14 @@ int generic_buffers_fsync_noflush(struct file *file, loff_t start, loff_t end,
ret = err;
return ret;
}
-EXPORT_SYMBOL(generic_buffers_fsync_noflush);
+EXPORT_SYMBOL(generic_mmb_fsync_noflush);
/**
- * generic_buffers_fsync - generic buffer fsync implementation
+ * generic_mmb_fsync - generic buffer fsync implementation
* for simple filesystems with no inode lock
*
* @file: file to synchronize
+ * @mmb: list of metadata bhs to flush
* @start: start offset in bytes
* @end: end offset in bytes (inclusive)
* @datasync: only synchronize essential metadata if true
@@ -685,18 +682,18 @@ EXPORT_SYMBOL(generic_buffers_fsync_noflush);
* hanging off the address_space structure. This also makes sure that
* a device cache flush operation is called at the end.
*/
-int generic_buffers_fsync(struct file *file, loff_t start, loff_t end,
- bool datasync)
+int generic_mmb_fsync(struct file *file, struct mapping_metadata_bhs *mmb,
+ loff_t start, loff_t end, bool datasync)
{
struct inode *inode = file->f_mapping->host;
int ret;
- ret = generic_buffers_fsync_noflush(file, start, end, datasync);
+ ret = generic_mmb_fsync_noflush(file, mmb, start, end, datasync);
if (!ret)
ret = blkdev_issue_flush(inode->i_sb->s_bdev);
return ret;
}
-EXPORT_SYMBOL(generic_buffers_fsync);
+EXPORT_SYMBOL(generic_mmb_fsync);
/*
* Called when we've recently written block `bblock', and it is known that
@@ -717,20 +714,18 @@ void write_boundary_block(struct block_device *bdev,
}
}
-void mark_buffer_dirty_inode(struct buffer_head *bh, struct inode *inode)
+void mmb_mark_buffer_dirty(struct buffer_head *bh,
+ struct mapping_metadata_bhs *mmb)
{
- struct address_space *mapping = inode->i_mapping;
-
mark_buffer_dirty(bh);
if (!bh->b_mmb) {
- spin_lock(&mapping->i_metadata_bhs.lock);
- list_move_tail(&bh->b_assoc_buffers,
- &mapping->i_metadata_bhs.list);
- bh->b_mmb = &mapping->i_metadata_bhs;
- spin_unlock(&mapping->i_metadata_bhs.lock);
+ spin_lock(&mmb->lock);
+ list_move_tail(&bh->b_assoc_buffers, &mmb->list);
+ bh->b_mmb = mmb;
+ spin_unlock(&mmb->lock);
}
}
-EXPORT_SYMBOL(mark_buffer_dirty_inode);
+EXPORT_SYMBOL(mmb_mark_buffer_dirty);
/**
* block_dirty_folio - Mark a folio as dirty.
@@ -797,14 +792,12 @@ bool block_dirty_folio(struct address_space *mapping, struct folio *folio)
EXPORT_SYMBOL(block_dirty_folio);
/*
- * Invalidate any and all dirty buffers on a given inode. We are
+ * Invalidate any and all dirty buffers on a given buffers list. We are
* probably unmounting the fs, but that doesn't mean we have already
* done a sync(). Just drop the buffers from the inode list.
*/
-void invalidate_inode_buffers(struct inode *inode)
+void mmb_invalidate_buffers(struct mapping_metadata_bhs *mmb)
{
- struct mapping_metadata_bhs *mmb = &inode->i_data.i_metadata_bhs;
-
if (mmb_has_buffers(mmb)) {
spin_lock(&mmb->lock);
while (!list_empty(&mmb->list))
@@ -812,7 +805,7 @@ void invalidate_inode_buffers(struct inode *inode)
spin_unlock(&mmb->lock);
}
}
-EXPORT_SYMBOL(invalidate_inode_buffers);
+EXPORT_SYMBOL(mmb_invalidate_buffers);
/*
* Create the appropriate buffers when given a folio for data area and
diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h
index 44094fd476f5..399277c679eb 100644
--- a/include/linux/buffer_head.h
+++ b/include/linux/buffer_head.h
@@ -205,12 +205,31 @@ 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 generic_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 generic_mmb_fsync_noflush(file, &file->f_mapping->i_metadata_bhs,
+ start, end, datasync);
+}
+int generic_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 generic_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 +534,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_buffers(struct mapping_metadata_bhs *mmb);
+int mmb_sync_buffers(struct mapping_metadata_bhs *mmb);
+static inline void invalidate_inode_buffers(struct inode *inode)
+{
+ mmb_invalidate_buffers(&inode->i_data.i_metadata_bhs);
+}
+static inline int sync_mapping_buffers(struct address_space *mapping)
+{
+ return mmb_sync_buffers(&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 +555,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_buffers(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) {}
--
2.51.0
^ permalink raw reply related
* [PATCH 32/41] ext2: Track metadata bhs in fs-private inode part
From: Jan Kara @ 2026-03-20 13:41 UTC (permalink / raw)
To: linux-fsdevel
Cc: linux-block, Christian Brauner, Al Viro, linux-ext4, Ted Tso,
Tigran A. Aivazian, David Sterba, OGAWA Hirofumi, Muchun Song,
Oscar Salvador, David Hildenbrand, linux-mm, linux-aio,
Benjamin LaHaise, Jan Kara
In-Reply-To: <20260320131728.6449-1-jack@suse.cz>
Track metadata bhs for an inode in fs-private part of the inode.
Signed-off-by: Jan Kara <jack@suse.cz>
---
fs/ext2/ext2.h | 1 +
fs/ext2/file.c | 6 ++++--
fs/ext2/inode.c | 16 +++++++++-------
fs/ext2/super.c | 1 +
4 files changed, 15 insertions(+), 9 deletions(-)
diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h
index 5e0c6c5fcb6c..3eb1f342645c 100644
--- a/fs/ext2/ext2.h
+++ b/fs/ext2/ext2.h
@@ -676,6 +676,7 @@ struct ext2_inode_info {
#ifdef CONFIG_QUOTA
struct dquot __rcu *i_dquot[MAXQUOTAS];
#endif
+ struct mapping_metadata_bhs i_metadata_bhs;
};
/*
diff --git a/fs/ext2/file.c b/fs/ext2/file.c
index ebe356a38b18..629133f0e8ae 100644
--- a/fs/ext2/file.c
+++ b/fs/ext2/file.c
@@ -156,9 +156,11 @@ static int ext2_release_file (struct inode * inode, struct file * filp)
int ext2_fsync(struct file *file, loff_t start, loff_t end, int datasync)
{
int ret;
- struct super_block *sb = file->f_mapping->host->i_sb;
+ struct inode *inode = file->f_mapping->host;
+ struct super_block *sb = inode->i_sb;
- ret = generic_buffers_fsync(file, start, end, datasync);
+ ret = generic_mmb_fsync(file, &EXT2_I(inode)->i_metadata_bhs,
+ start, end, datasync);
if (ret == -EIO)
/* We don't really know where the IO error happened... */
ext2_error(sb, __func__,
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index fb91c61aa6d6..dfed87fbbccd 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -95,9 +95,9 @@ void ext2_evict_inode(struct inode * inode)
ext2_truncate_blocks(inode, 0);
ext2_xattr_delete_inode(inode);
} else {
- sync_mapping_buffers(&inode->i_data);
+ mmb_sync_buffers(&EXT2_I(inode)->i_metadata_bhs);
}
- invalidate_inode_buffers(inode);
+ mmb_invalidate_buffers(&EXT2_I(inode)->i_metadata_bhs);
clear_inode(inode);
ext2_discard_reservation(inode);
@@ -527,7 +527,7 @@ static int ext2_alloc_branch(struct inode *inode,
}
set_buffer_uptodate(bh);
unlock_buffer(bh);
- mark_buffer_dirty_inode(bh, inode);
+ mmb_mark_buffer_dirty(bh, &EXT2_I(inode)->i_metadata_bhs);
/* We used to sync bh here if IS_SYNC(inode).
* But we now rely upon generic_write_sync()
* and b_inode_buffers. But not for directories.
@@ -598,7 +598,7 @@ static void ext2_splice_branch(struct inode *inode,
/* had we spliced it onto indirect block? */
if (where->bh)
- mark_buffer_dirty_inode(where->bh, inode);
+ mmb_mark_buffer_dirty(where->bh, &EXT2_I(inode)->i_metadata_bhs);
inode_set_ctime_current(inode);
mark_inode_dirty(inode);
@@ -1211,7 +1211,8 @@ static void __ext2_truncate_blocks(struct inode *inode, loff_t offset)
if (partial == chain)
mark_inode_dirty(inode);
else
- mark_buffer_dirty_inode(partial->bh, inode);
+ mmb_mark_buffer_dirty(partial->bh,
+ &EXT2_I(inode)->i_metadata_bhs);
ext2_free_branches(inode, &nr, &nr+1, (chain+n-1) - partial);
}
/* Clear the ends of indirect blocks on the shared branch */
@@ -1220,7 +1221,8 @@ static void __ext2_truncate_blocks(struct inode *inode, loff_t offset)
partial->p + 1,
(__le32*)partial->bh->b_data+addr_per_block,
(chain+n-1) - partial);
- mark_buffer_dirty_inode(partial->bh, inode);
+ mmb_mark_buffer_dirty(partial->bh,
+ &EXT2_I(inode)->i_metadata_bhs);
brelse (partial->bh);
partial--;
}
@@ -1303,7 +1305,7 @@ static int ext2_setsize(struct inode *inode, loff_t newsize)
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
if (inode_needs_sync(inode)) {
- sync_mapping_buffers(inode->i_mapping);
+ mmb_sync_buffers(&EXT2_I(inode)->i_metadata_bhs);
sync_inode_metadata(inode, 1);
} else {
mark_inode_dirty(inode);
diff --git a/fs/ext2/super.c b/fs/ext2/super.c
index 603f2641fe10..4118a3a1f620 100644
--- a/fs/ext2/super.c
+++ b/fs/ext2/super.c
@@ -215,6 +215,7 @@ static struct inode *ext2_alloc_inode(struct super_block *sb)
#ifdef CONFIG_QUOTA
memset(&ei->i_dquot, 0, sizeof(ei->i_dquot));
#endif
+ mmb_init(&ei->i_metadata_bhs, &ei->vfs_inode.i_data);
return &ei->vfs_inode;
}
--
2.51.0
^ permalink raw reply related
* [PATCH 33/41] affs: Track metadata bhs in fs-private inode part
From: Jan Kara @ 2026-03-20 13:41 UTC (permalink / raw)
To: linux-fsdevel
Cc: linux-block, Christian Brauner, Al Viro, linux-ext4, Ted Tso,
Tigran A. Aivazian, David Sterba, OGAWA Hirofumi, Muchun Song,
Oscar Salvador, David Hildenbrand, linux-mm, linux-aio,
Benjamin LaHaise, Jan Kara
In-Reply-To: <20260320131728.6449-1-jack@suse.cz>
Track metadata bhs for an inode in fs-private part of the inode.
Signed-off-by: Jan Kara <jack@suse.cz>
---
fs/affs/affs.h | 2 ++
fs/affs/amigaffs.c | 12 ++++++------
fs/affs/file.c | 25 ++++++++++++++-----------
fs/affs/inode.c | 14 +++++++-------
fs/affs/namei.c | 9 +++++----
fs/affs/super.c | 1 +
6 files changed, 35 insertions(+), 28 deletions(-)
diff --git a/fs/affs/affs.h b/fs/affs/affs.h
index ac4e9a02910b..a1eb400e1018 100644
--- a/fs/affs/affs.h
+++ b/fs/affs/affs.h
@@ -44,6 +44,7 @@ struct affs_inode_info {
struct mutex i_link_lock; /* Protects internal inode access. */
struct mutex i_ext_lock; /* Protects internal inode access. */
#define i_hash_lock i_ext_lock
+ struct mapping_metadata_bhs i_metadata_bhs;
u32 i_blkcnt; /* block count */
u32 i_extcnt; /* extended block count */
u32 *i_lc; /* linear cache of extended blocks */
@@ -151,6 +152,7 @@ extern bool affs_nofilenametruncate(const struct dentry *dentry);
extern int affs_check_name(const unsigned char *name, int len,
bool notruncate);
extern int affs_copy_name(unsigned char *bstr, struct dentry *dentry);
+struct mapping_metadata_bhs *affs_get_metadata_bhs(struct inode *inode);
/* bitmap. c */
diff --git a/fs/affs/amigaffs.c b/fs/affs/amigaffs.c
index fd669daa4e7b..13a914c1d8b7 100644
--- a/fs/affs/amigaffs.c
+++ b/fs/affs/amigaffs.c
@@ -57,7 +57,7 @@ affs_insert_hash(struct inode *dir, struct buffer_head *bh)
AFFS_TAIL(sb, dir_bh)->hash_chain = cpu_to_be32(ino);
affs_adjust_checksum(dir_bh, ino);
- mark_buffer_dirty_inode(dir_bh, dir);
+ mmb_mark_buffer_dirty(dir_bh, &AFFS_I(dir)->i_metadata_bhs);
affs_brelse(dir_bh);
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
@@ -100,7 +100,7 @@ affs_remove_hash(struct inode *dir, struct buffer_head *rem_bh)
else
AFFS_TAIL(sb, bh)->hash_chain = ino;
affs_adjust_checksum(bh, be32_to_cpu(ino) - hash_ino);
- mark_buffer_dirty_inode(bh, dir);
+ mmb_mark_buffer_dirty(bh, &AFFS_I(dir)->i_metadata_bhs);
AFFS_TAIL(sb, rem_bh)->parent = 0;
retval = 0;
break;
@@ -180,7 +180,7 @@ affs_remove_link(struct dentry *dentry)
affs_unlock_dir(dir);
goto done;
}
- mark_buffer_dirty_inode(link_bh, inode);
+ mmb_mark_buffer_dirty(link_bh, &AFFS_I(inode)->i_metadata_bhs);
memcpy(AFFS_TAIL(sb, bh)->name, AFFS_TAIL(sb, link_bh)->name, 32);
retval = affs_insert_hash(dir, bh);
@@ -188,7 +188,7 @@ affs_remove_link(struct dentry *dentry)
affs_unlock_dir(dir);
goto done;
}
- mark_buffer_dirty_inode(bh, inode);
+ mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
affs_unlock_dir(dir);
iput(dir);
@@ -203,7 +203,7 @@ affs_remove_link(struct dentry *dentry)
__be32 ino2 = AFFS_TAIL(sb, link_bh)->link_chain;
AFFS_TAIL(sb, bh)->link_chain = ino2;
affs_adjust_checksum(bh, be32_to_cpu(ino2) - link_ino);
- mark_buffer_dirty_inode(bh, inode);
+ mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
retval = 0;
/* Fix the link count, if bh is a normal header block without links */
switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) {
@@ -306,7 +306,7 @@ affs_remove_header(struct dentry *dentry)
retval = affs_remove_hash(dir, bh);
if (retval)
goto done_unlock;
- mark_buffer_dirty_inode(bh, inode);
+ mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
affs_unlock_dir(dir);
diff --git a/fs/affs/file.c b/fs/affs/file.c
index 6c9258359ddb..606630d6f5f7 100644
--- a/fs/affs/file.c
+++ b/fs/affs/file.c
@@ -140,14 +140,14 @@ affs_alloc_extblock(struct inode *inode, struct buffer_head *bh, u32 ext)
AFFS_TAIL(sb, new_bh)->parent = cpu_to_be32(inode->i_ino);
affs_fix_checksum(sb, new_bh);
- mark_buffer_dirty_inode(new_bh, inode);
+ mmb_mark_buffer_dirty(new_bh, &AFFS_I(inode)->i_metadata_bhs);
tmp = be32_to_cpu(AFFS_TAIL(sb, bh)->extension);
if (tmp)
affs_warning(sb, "alloc_ext", "previous extension set (%x)", tmp);
AFFS_TAIL(sb, bh)->extension = cpu_to_be32(blocknr);
affs_adjust_checksum(bh, blocknr - tmp);
- mark_buffer_dirty_inode(bh, inode);
+ mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
AFFS_I(inode)->i_extcnt++;
mark_inode_dirty(inode);
@@ -581,7 +581,7 @@ affs_extent_file_ofs(struct inode *inode, u32 newsize)
memset(AFFS_DATA(bh) + boff, 0, tmp);
be32_add_cpu(&AFFS_DATA_HEAD(bh)->size, tmp);
affs_fix_checksum(sb, bh);
- mark_buffer_dirty_inode(bh, inode);
+ mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
size += tmp;
bidx++;
} else if (bidx) {
@@ -603,7 +603,7 @@ affs_extent_file_ofs(struct inode *inode, u32 newsize)
AFFS_DATA_HEAD(bh)->size = cpu_to_be32(tmp);
affs_fix_checksum(sb, bh);
bh->b_state &= ~(1UL << BH_New);
- mark_buffer_dirty_inode(bh, inode);
+ mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
if (prev_bh) {
u32 tmp_next = be32_to_cpu(AFFS_DATA_HEAD(prev_bh)->next);
@@ -613,7 +613,8 @@ affs_extent_file_ofs(struct inode *inode, u32 newsize)
bidx, tmp_next);
AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr);
affs_adjust_checksum(prev_bh, bh->b_blocknr - tmp_next);
- mark_buffer_dirty_inode(prev_bh, inode);
+ mmb_mark_buffer_dirty(prev_bh,
+ &AFFS_I(inode)->i_metadata_bhs);
affs_brelse(prev_bh);
}
size += bsize;
@@ -732,7 +733,7 @@ static int affs_write_end_ofs(const struct kiocb *iocb,
AFFS_DATA_HEAD(bh)->size = cpu_to_be32(
max(boff + tmp, be32_to_cpu(AFFS_DATA_HEAD(bh)->size)));
affs_fix_checksum(sb, bh);
- mark_buffer_dirty_inode(bh, inode);
+ mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
written += tmp;
from += tmp;
bidx++;
@@ -765,12 +766,13 @@ static int affs_write_end_ofs(const struct kiocb *iocb,
bidx, tmp_next);
AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr);
affs_adjust_checksum(prev_bh, bh->b_blocknr - tmp_next);
- mark_buffer_dirty_inode(prev_bh, inode);
+ mmb_mark_buffer_dirty(prev_bh,
+ &AFFS_I(inode)->i_metadata_bhs);
}
}
affs_brelse(prev_bh);
affs_fix_checksum(sb, bh);
- mark_buffer_dirty_inode(bh, inode);
+ mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
written += bsize;
from += bsize;
bidx++;
@@ -799,13 +801,14 @@ static int affs_write_end_ofs(const struct kiocb *iocb,
bidx, tmp_next);
AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr);
affs_adjust_checksum(prev_bh, bh->b_blocknr - tmp_next);
- mark_buffer_dirty_inode(prev_bh, inode);
+ mmb_mark_buffer_dirty(prev_bh,
+ &AFFS_I(inode)->i_metadata_bhs);
}
} else if (be32_to_cpu(AFFS_DATA_HEAD(bh)->size) < tmp)
AFFS_DATA_HEAD(bh)->size = cpu_to_be32(tmp);
affs_brelse(prev_bh);
affs_fix_checksum(sb, bh);
- mark_buffer_dirty_inode(bh, inode);
+ mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
written += tmp;
from += tmp;
bidx++;
@@ -942,7 +945,7 @@ affs_truncate(struct inode *inode)
}
AFFS_TAIL(sb, ext_bh)->extension = 0;
affs_fix_checksum(sb, ext_bh);
- mark_buffer_dirty_inode(ext_bh, inode);
+ mmb_mark_buffer_dirty(ext_bh, &AFFS_I(inode)->i_metadata_bhs);
affs_brelse(ext_bh);
if (inode->i_size) {
diff --git a/fs/affs/inode.c b/fs/affs/inode.c
index 84afa862f220..e62c5a79efd6 100644
--- a/fs/affs/inode.c
+++ b/fs/affs/inode.c
@@ -206,7 +206,7 @@ affs_write_inode(struct inode *inode, struct writeback_control *wbc)
}
}
affs_fix_checksum(sb, bh);
- mark_buffer_dirty_inode(bh, inode);
+ mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
affs_brelse(bh);
affs_free_prealloc(inode);
return 0;
@@ -268,10 +268,10 @@ affs_evict_inode(struct inode *inode)
inode->i_size = 0;
affs_truncate(inode);
} else {
- sync_mapping_buffers(&inode->i_data);
+ mmb_sync_buffers(&AFFS_I(inode)->i_metadata_bhs);
}
- invalidate_inode_buffers(inode);
+ mmb_invalidate_buffers(&AFFS_I(inode)->i_metadata_bhs);
clear_inode(inode);
affs_free_prealloc(inode);
cache_page = (unsigned long)AFFS_I(inode)->i_lc;
@@ -306,7 +306,7 @@ affs_new_inode(struct inode *dir)
bh = affs_getzeroblk(sb, block);
if (!bh)
goto err_bh;
- mark_buffer_dirty_inode(bh, inode);
+ mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
affs_brelse(bh);
inode->i_uid = current_fsuid();
@@ -394,17 +394,17 @@ affs_add_entry(struct inode *dir, struct inode *inode, struct dentry *dentry, s3
AFFS_TAIL(sb, bh)->link_chain = chain;
AFFS_TAIL(sb, inode_bh)->link_chain = cpu_to_be32(block);
affs_adjust_checksum(inode_bh, block - be32_to_cpu(chain));
- mark_buffer_dirty_inode(inode_bh, inode);
+ mmb_mark_buffer_dirty(inode_bh, &AFFS_I(inode)->i_metadata_bhs);
set_nlink(inode, 2);
ihold(inode);
}
affs_fix_checksum(sb, bh);
- mark_buffer_dirty_inode(bh, inode);
+ mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
dentry->d_fsdata = (void *)(long)bh->b_blocknr;
affs_lock_dir(dir);
retval = affs_insert_hash(dir, bh);
- mark_buffer_dirty_inode(bh, inode);
+ mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
affs_unlock_dir(dir);
affs_unlock_link(inode);
diff --git a/fs/affs/namei.c b/fs/affs/namei.c
index f883be50db12..23d00d85cf21 100644
--- a/fs/affs/namei.c
+++ b/fs/affs/namei.c
@@ -373,7 +373,7 @@ affs_symlink(struct mnt_idmap *idmap, struct inode *dir,
}
*p = 0;
inode->i_size = i + 1;
- mark_buffer_dirty_inode(bh, inode);
+ mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
affs_brelse(bh);
mark_inode_dirty(inode);
@@ -443,7 +443,8 @@ affs_rename(struct inode *old_dir, struct dentry *old_dentry,
/* TODO: move it back to old_dir, if error? */
done:
- mark_buffer_dirty_inode(bh, retval ? old_dir : new_dir);
+ mmb_mark_buffer_dirty(bh,
+ &AFFS_I(retval ? old_dir : new_dir)->i_metadata_bhs);
affs_brelse(bh);
return retval;
}
@@ -496,8 +497,8 @@ affs_xrename(struct inode *old_dir, struct dentry *old_dentry,
retval = affs_insert_hash(old_dir, bh_new);
affs_unlock_dir(old_dir);
done:
- mark_buffer_dirty_inode(bh_old, new_dir);
- mark_buffer_dirty_inode(bh_new, old_dir);
+ mmb_mark_buffer_dirty(bh_old, &AFFS_I(new_dir)->i_metadata_bhs);
+ mmb_mark_buffer_dirty(bh_new, &AFFS_I(old_dir)->i_metadata_bhs);
affs_brelse(bh_old);
affs_brelse(bh_new);
return retval;
diff --git a/fs/affs/super.c b/fs/affs/super.c
index 8451647f3fea..079f36e1ddec 100644
--- a/fs/affs/super.c
+++ b/fs/affs/super.c
@@ -108,6 +108,7 @@ static struct inode *affs_alloc_inode(struct super_block *sb)
i->i_lc = NULL;
i->i_ext_bh = NULL;
i->i_pa_cnt = 0;
+ mmb_init(&i->i_metadata_bhs, &i->vfs_inode.i_data);
return &i->vfs_inode;
}
--
2.51.0
^ permalink raw reply related
* [PATCH 35/41] fat: Track metadata bhs in fs-private inode part
From: Jan Kara @ 2026-03-20 13:41 UTC (permalink / raw)
To: linux-fsdevel
Cc: linux-block, Christian Brauner, Al Viro, linux-ext4, Ted Tso,
Tigran A. Aivazian, David Sterba, OGAWA Hirofumi, Muchun Song,
Oscar Salvador, David Hildenbrand, linux-mm, linux-aio,
Benjamin LaHaise, Jan Kara
In-Reply-To: <20260320131728.6449-1-jack@suse.cz>
Track metadata bhs for an inode in fs-private part of the inode.
Signed-off-by: Jan Kara <jack@suse.cz>
---
fs/fat/dir.c | 17 ++++++++++-------
fs/fat/fat.h | 1 +
fs/fat/fatent.c | 15 ++++++++++-----
fs/fat/file.c | 8 +++++---
fs/fat/inode.c | 5 +++--
fs/fat/namei_msdos.c | 6 ++++--
fs/fat/namei_vfat.c | 2 +-
7 files changed, 34 insertions(+), 20 deletions(-)
diff --git a/fs/fat/dir.c b/fs/fat/dir.c
index 4b8b25f688e4..4f6f42f33613 100644
--- a/fs/fat/dir.c
+++ b/fs/fat/dir.c
@@ -1027,7 +1027,7 @@ static int __fat_remove_entries(struct inode *dir, loff_t pos, int nr_slots)
de++;
nr_slots--;
}
- mark_buffer_dirty_inode(bh, dir);
+ mmb_mark_buffer_dirty(bh, &MSDOS_I(dir)->i_metadata_bhs);
if (IS_DIRSYNC(dir))
err = sync_dirty_buffer(bh);
brelse(bh);
@@ -1062,7 +1062,7 @@ int fat_remove_entries(struct inode *dir, struct fat_slot_info *sinfo)
de--;
nr_slots--;
}
- mark_buffer_dirty_inode(bh, dir);
+ mmb_mark_buffer_dirty(bh, &MSDOS_I(dir)->i_metadata_bhs);
if (IS_DIRSYNC(dir))
err = sync_dirty_buffer(bh);
brelse(bh);
@@ -1114,7 +1114,7 @@ static int fat_zeroed_cluster(struct inode *dir, sector_t blknr, int nr_used,
memset(bhs[n]->b_data, 0, sb->s_blocksize);
set_buffer_uptodate(bhs[n]);
unlock_buffer(bhs[n]);
- mark_buffer_dirty_inode(bhs[n], dir);
+ mmb_mark_buffer_dirty(bhs[n], &MSDOS_I(dir)->i_metadata_bhs);
n++;
blknr++;
@@ -1195,7 +1195,7 @@ int fat_alloc_new_dir(struct inode *dir, struct timespec64 *ts)
memset(de + 2, 0, sb->s_blocksize - 2 * sizeof(*de));
set_buffer_uptodate(bhs[0]);
unlock_buffer(bhs[0]);
- mark_buffer_dirty_inode(bhs[0], dir);
+ mmb_mark_buffer_dirty(bhs[0], &MSDOS_I(dir)->i_metadata_bhs);
err = fat_zeroed_cluster(dir, blknr, 1, bhs, MAX_BUF_PER_PAGE);
if (err)
@@ -1257,7 +1257,8 @@ static int fat_add_new_entries(struct inode *dir, void *slots, int nr_slots,
memcpy(bhs[n]->b_data, slots, copy);
set_buffer_uptodate(bhs[n]);
unlock_buffer(bhs[n]);
- mark_buffer_dirty_inode(bhs[n], dir);
+ mmb_mark_buffer_dirty(bhs[n],
+ &MSDOS_I(dir)->i_metadata_bhs);
slots += copy;
size -= copy;
if (!size)
@@ -1358,7 +1359,8 @@ int fat_add_entries(struct inode *dir, void *slots, int nr_slots,
for (i = 0; i < long_bhs; i++) {
int copy = umin(sb->s_blocksize - offset, size);
memcpy(bhs[i]->b_data + offset, slots, copy);
- mark_buffer_dirty_inode(bhs[i], dir);
+ mmb_mark_buffer_dirty(bhs[i],
+ &MSDOS_I(dir)->i_metadata_bhs);
offset = 0;
slots += copy;
size -= copy;
@@ -1369,7 +1371,8 @@ int fat_add_entries(struct inode *dir, void *slots, int nr_slots,
/* Fill the short name slot. */
int copy = umin(sb->s_blocksize - offset, size);
memcpy(bhs[i]->b_data + offset, slots, copy);
- mark_buffer_dirty_inode(bhs[i], dir);
+ mmb_mark_buffer_dirty(bhs[i],
+ &MSDOS_I(dir)->i_metadata_bhs);
if (IS_DIRSYNC(dir))
err = sync_dirty_buffer(bhs[i]);
}
diff --git a/fs/fat/fat.h b/fs/fat/fat.h
index 0d269dba897b..5a58f0bf8ce8 100644
--- a/fs/fat/fat.h
+++ b/fs/fat/fat.h
@@ -130,6 +130,7 @@ struct msdos_inode_info {
struct hlist_node i_dir_hash; /* hash by i_logstart */
struct rw_semaphore truncate_lock; /* protect bmap against truncate */
struct timespec64 i_crtime; /* File creation (birth) time */
+ struct mapping_metadata_bhs i_metadata_bhs;
struct inode vfs_inode;
};
diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c
index a7061c2ad8e4..f0801d99dd62 100644
--- a/fs/fat/fatent.c
+++ b/fs/fat/fatent.c
@@ -170,9 +170,11 @@ static void fat12_ent_put(struct fat_entry *fatent, int new)
}
spin_unlock(&fat12_entry_lock);
- mark_buffer_dirty_inode(fatent->bhs[0], fatent->fat_inode);
+ mmb_mark_buffer_dirty(fatent->bhs[0],
+ &MSDOS_I(fatent->fat_inode)->i_metadata_bhs);
if (fatent->nr_bhs == 2)
- mark_buffer_dirty_inode(fatent->bhs[1], fatent->fat_inode);
+ mmb_mark_buffer_dirty(fatent->bhs[1],
+ &MSDOS_I(fatent->fat_inode)->i_metadata_bhs);
}
static void fat16_ent_put(struct fat_entry *fatent, int new)
@@ -181,7 +183,8 @@ static void fat16_ent_put(struct fat_entry *fatent, int new)
new = EOF_FAT16;
*fatent->u.ent16_p = cpu_to_le16(new);
- mark_buffer_dirty_inode(fatent->bhs[0], fatent->fat_inode);
+ mmb_mark_buffer_dirty(fatent->bhs[0],
+ &MSDOS_I(fatent->fat_inode)->i_metadata_bhs);
}
static void fat32_ent_put(struct fat_entry *fatent, int new)
@@ -189,7 +192,8 @@ static void fat32_ent_put(struct fat_entry *fatent, int new)
WARN_ON(new & 0xf0000000);
new |= le32_to_cpu(*fatent->u.ent32_p) & ~0x0fffffff;
*fatent->u.ent32_p = cpu_to_le32(new);
- mark_buffer_dirty_inode(fatent->bhs[0], fatent->fat_inode);
+ mmb_mark_buffer_dirty(fatent->bhs[0],
+ &MSDOS_I(fatent->fat_inode)->i_metadata_bhs);
}
static int fat12_ent_next(struct fat_entry *fatent)
@@ -395,7 +399,8 @@ static int fat_mirror_bhs(struct super_block *sb, struct buffer_head **bhs,
memcpy(c_bh->b_data, bhs[n]->b_data, sb->s_blocksize);
set_buffer_uptodate(c_bh);
unlock_buffer(c_bh);
- mark_buffer_dirty_inode(c_bh, sbi->fat_inode);
+ mmb_mark_buffer_dirty(c_bh,
+ &MSDOS_I(sbi->fat_inode)->i_metadata_bhs);
if (sb->s_flags & SB_SYNCHRONOUS)
err = sync_dirty_buffer(c_bh);
brelse(c_bh);
diff --git a/fs/fat/file.c b/fs/fat/file.c
index 1551065a7964..3bac06b41420 100644
--- a/fs/fat/file.c
+++ b/fs/fat/file.c
@@ -186,13 +186,15 @@ static int fat_file_release(struct inode *inode, struct file *filp)
int fat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync)
{
struct inode *inode = filp->f_mapping->host;
+ struct inode *fat_inode = MSDOS_SB(inode->i_sb)->fat_inode;
int err;
- err = generic_buffers_fsync_noflush(filp, start, end, datasync);
+ err = generic_mmb_fsync_noflush(filp, &MSDOS_I(inode)->i_metadata_bhs,
+ start, end, datasync);
if (err)
return err;
- err = sync_mapping_buffers(MSDOS_SB(inode->i_sb)->fat_inode->i_mapping);
+ err = mmb_sync_buffers(&MSDOS_I(fat_inode)->i_metadata_bhs);
if (err)
return err;
@@ -236,7 +238,7 @@ static int fat_cont_expand(struct inode *inode, loff_t size)
*/
err = filemap_fdatawrite_range(mapping, start,
start + count - 1);
- err2 = sync_mapping_buffers(mapping);
+ err2 = mmb_sync_buffers(&MSDOS_I(inode)->i_metadata_bhs);
if (!err)
err = err2;
err2 = write_inode_now(inode, 1);
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index ce88602b0d57..1e54091c80fc 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -658,11 +658,11 @@ static void fat_evict_inode(struct inode *inode)
inode->i_size = 0;
fat_truncate_blocks(inode, 0);
} else {
- sync_mapping_buffers(inode->i_mapping);
+ mmb_sync_buffers(&MSDOS_I(inode)->i_metadata_bhs);
fat_free_eofblocks(inode);
}
- invalidate_inode_buffers(inode);
+ mmb_invalidate_buffers(&MSDOS_I(inode)->i_metadata_bhs);
clear_inode(inode);
fat_cache_inval_inode(inode);
fat_detach(inode);
@@ -763,6 +763,7 @@ static struct inode *fat_alloc_inode(struct super_block *sb)
ei->i_pos = 0;
ei->i_crtime.tv_sec = 0;
ei->i_crtime.tv_nsec = 0;
+ mmb_init(&ei->i_metadata_bhs, &ei->vfs_inode.i_data);
return &ei->vfs_inode;
}
diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c
index 048c103b506a..4cc65f330fb7 100644
--- a/fs/fat/namei_msdos.c
+++ b/fs/fat/namei_msdos.c
@@ -527,7 +527,8 @@ static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,
if (update_dotdot) {
fat_set_start(dotdot_de, MSDOS_I(new_dir)->i_logstart);
- mark_buffer_dirty_inode(dotdot_bh, old_inode);
+ mmb_mark_buffer_dirty(dotdot_bh,
+ &MSDOS_I(old_inode)->i_metadata_bhs);
if (IS_DIRSYNC(new_dir)) {
err = sync_dirty_buffer(dotdot_bh);
if (err)
@@ -566,7 +567,8 @@ static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,
if (update_dotdot) {
fat_set_start(dotdot_de, MSDOS_I(old_dir)->i_logstart);
- mark_buffer_dirty_inode(dotdot_bh, old_inode);
+ mmb_mark_buffer_dirty(dotdot_bh,
+ &MSDOS_I(old_inode)->i_metadata_bhs);
corrupt |= sync_dirty_buffer(dotdot_bh);
}
error_inode:
diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c
index 87dcdd86272b..918b3756674c 100644
--- a/fs/fat/namei_vfat.c
+++ b/fs/fat/namei_vfat.c
@@ -915,7 +915,7 @@ static int vfat_update_dotdot_de(struct inode *dir, struct inode *inode,
struct msdos_dir_entry *dotdot_de)
{
fat_set_start(dotdot_de, MSDOS_I(dir)->i_logstart);
- mark_buffer_dirty_inode(dotdot_bh, inode);
+ mmb_mark_buffer_dirty(dotdot_bh, &MSDOS_I(inode)->i_metadata_bhs);
if (IS_DIRSYNC(dir))
return sync_dirty_buffer(dotdot_bh);
return 0;
--
2.51.0
^ permalink raw reply related
* [PATCH 34/41] bfs: Track metadata bhs in fs-private inode part
From: Jan Kara @ 2026-03-20 13:41 UTC (permalink / raw)
To: linux-fsdevel
Cc: linux-block, Christian Brauner, Al Viro, linux-ext4, Ted Tso,
Tigran A. Aivazian, David Sterba, OGAWA Hirofumi, Muchun Song,
Oscar Salvador, David Hildenbrand, linux-mm, linux-aio,
Benjamin LaHaise, Jan Kara
In-Reply-To: <20260320131728.6449-1-jack@suse.cz>
Track metadata bhs for an inode in fs-private part of the inode.
Signed-off-by: Jan Kara <jack@suse.cz>
---
fs/bfs/bfs.h | 1 +
fs/bfs/dir.c | 16 ++++++++++++----
fs/bfs/inode.c | 6 ++++--
3 files changed, 17 insertions(+), 6 deletions(-)
diff --git a/fs/bfs/bfs.h b/fs/bfs/bfs.h
index 606f9378b2f0..b08afe733e63 100644
--- a/fs/bfs/bfs.h
+++ b/fs/bfs/bfs.h
@@ -35,6 +35,7 @@ struct bfs_inode_info {
unsigned long i_dsk_ino; /* inode number from the disk, can be 0 */
unsigned long i_sblock;
unsigned long i_eblock;
+ struct mapping_metadata_bhs i_metadata_bhs;
struct inode vfs_inode;
};
diff --git a/fs/bfs/dir.c b/fs/bfs/dir.c
index 1b140981dbf3..1dbce745d1ad 100644
--- a/fs/bfs/dir.c
+++ b/fs/bfs/dir.c
@@ -68,10 +68,17 @@ static int bfs_readdir(struct file *f, struct dir_context *ctx)
return 0;
}
+static int bfs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
+{
+ return generic_mmb_fsync(file,
+ &BFS_I(file->f_mapping->host)->i_metadata_bhs,
+ start, end, datasync);
+}
+
const struct file_operations bfs_dir_operations = {
.read = generic_read_dir,
.iterate_shared = bfs_readdir,
- .fsync = generic_buffers_fsync,
+ .fsync = bfs_fsync,
.llseek = generic_file_llseek,
};
@@ -186,7 +193,7 @@ static int bfs_unlink(struct inode *dir, struct dentry *dentry)
set_nlink(inode, 1);
}
de->ino = 0;
- mark_buffer_dirty_inode(bh, dir);
+ mmb_mark_buffer_dirty(bh, &BFS_I(dir)->i_metadata_bhs);
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
mark_inode_dirty(dir);
inode_set_ctime_to_ts(inode, inode_get_ctime(dir));
@@ -246,7 +253,7 @@ static int bfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
inode_set_ctime_current(new_inode);
inode_dec_link_count(new_inode);
}
- mark_buffer_dirty_inode(old_bh, old_dir);
+ mmb_mark_buffer_dirty(old_bh, &BFS_I(old_dir)->i_metadata_bhs);
error = 0;
end_rename:
@@ -296,7 +303,8 @@ static int bfs_add_entry(struct inode *dir, const struct qstr *child, int ino)
for (i = 0; i < BFS_NAMELEN; i++)
de->name[i] =
(i < namelen) ? name[i] : 0;
- mark_buffer_dirty_inode(bh, dir);
+ mmb_mark_buffer_dirty(bh,
+ &BFS_I(dir)->i_metadata_bhs);
brelse(bh);
return 0;
}
diff --git a/fs/bfs/inode.c b/fs/bfs/inode.c
index e0e50a9dbe9c..89f3da14e8c6 100644
--- a/fs/bfs/inode.c
+++ b/fs/bfs/inode.c
@@ -188,8 +188,8 @@ static void bfs_evict_inode(struct inode *inode)
truncate_inode_pages_final(&inode->i_data);
if (inode->i_nlink)
- sync_mapping_buffers(&inode->i_data);
- invalidate_inode_buffers(inode);
+ mmb_sync_buffers(&BFS_I(inode)->i_metadata_bhs);
+ mmb_invalidate_buffers(&BFS_I(inode)->i_metadata_bhs);
clear_inode(inode);
if (inode->i_nlink)
@@ -259,6 +259,8 @@ static struct inode *bfs_alloc_inode(struct super_block *sb)
bi = alloc_inode_sb(sb, bfs_inode_cachep, GFP_KERNEL);
if (!bi)
return NULL;
+ mmb_init(&bi->i_metadata_bhs, &bi->vfs_inode.i_data);
+
return &bi->vfs_inode;
}
--
2.51.0
^ permalink raw reply related
* [PATCH 38/41] ext4: Track metadata bhs in fs-private inode part
From: Jan Kara @ 2026-03-20 13:41 UTC (permalink / raw)
To: linux-fsdevel
Cc: linux-block, Christian Brauner, Al Viro, linux-ext4, Ted Tso,
Tigran A. Aivazian, David Sterba, OGAWA Hirofumi, Muchun Song,
Oscar Salvador, David Hildenbrand, linux-mm, linux-aio,
Benjamin LaHaise, Jan Kara
In-Reply-To: <20260320131728.6449-1-jack@suse.cz>
Track metadata bhs for an inode in fs-private part of the inode. We need
the tracking only for nojournal mode so this is somewhat wasteful. We
can relatively easily make the mapping_metadata_bhs struct dynamically
allocated similarly to how we treat jbd2_inode but let's leave that for
ext4 specific series once the dust settles a bit.
Signed-off-by: Jan Kara <jack@suse.cz>
---
fs/ext4/ext4.h | 1 +
fs/ext4/ext4_jbd2.c | 3 ++-
fs/ext4/fsync.c | 5 +++--
fs/ext4/inode.c | 4 ++--
fs/ext4/super.c | 3 ++-
5 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 293f698b7042..8df3617fd0e7 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1121,6 +1121,7 @@ struct ext4_inode_info {
struct rw_semaphore i_data_sem;
struct inode vfs_inode;
struct jbd2_inode *jinode;
+ struct mapping_metadata_bhs i_metadata_bhs;
/*
* File creation time. Its function is same as that of
diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c
index 05e5946ed9b3..9a8c225f2753 100644
--- a/fs/ext4/ext4_jbd2.c
+++ b/fs/ext4/ext4_jbd2.c
@@ -390,7 +390,8 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
}
} else {
if (inode)
- mark_buffer_dirty_inode(bh, inode);
+ mmb_mark_buffer_dirty(bh,
+ &EXT4_I(inode)->i_metadata_bhs);
else
mark_buffer_dirty(bh);
if (inode && inode_needs_sync(inode)) {
diff --git a/fs/ext4/fsync.c b/fs/ext4/fsync.c
index e476c6de3074..709c403273aa 100644
--- a/fs/ext4/fsync.c
+++ b/fs/ext4/fsync.c
@@ -68,7 +68,7 @@ static int ext4_sync_parent(struct inode *inode)
* through ext4_evict_inode()) and so we are safe to flush
* metadata blocks and the inode.
*/
- ret = sync_mapping_buffers(inode->i_mapping);
+ ret = mmb_sync_buffers(&EXT4_I(inode)->i_metadata_bhs);
if (ret)
break;
ret = sync_inode_metadata(inode, 1);
@@ -85,7 +85,8 @@ static int ext4_fsync_nojournal(struct file *file, loff_t start, loff_t end,
struct inode *inode = file->f_inode;
int ret;
- ret = generic_buffers_fsync_noflush(file, start, end, datasync);
+ ret = generic_mmb_fsync_noflush(file, &EXT4_I(inode)->i_metadata_bhs,
+ start, end, datasync);
if (!ret)
ret = ext4_sync_parent(inode);
if (test_opt(inode->i_sb, BARRIER))
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 011cb2eb16a2..abc17ef0c9ee 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -187,7 +187,7 @@ void ext4_evict_inode(struct inode *inode)
truncate_inode_pages_final(&inode->i_data);
/* Avoid mballoc special inode which has no proper iops */
if (!EXT4_SB(inode->i_sb)->s_journal)
- sync_mapping_buffers(&inode->i_data);
+ mmb_sync_buffers(&EXT4_I(inode)->i_metadata_bhs);
goto no_delete;
}
@@ -3436,7 +3436,7 @@ static bool ext4_inode_datasync_dirty(struct inode *inode)
}
/* Any metadata buffers to write? */
- if (mmb_has_buffers(&inode->i_mapping->i_metadata_bhs))
+ if (mmb_has_buffers(&EXT4_I(inode)->i_metadata_bhs))
return true;
return inode_state_read_once(inode) & I_DIRTY_DATASYNC;
}
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index ea827b0ecc8d..1b2b4ad62a10 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1428,6 +1428,7 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)
INIT_WORK(&ei->i_rsv_conversion_work, ext4_end_io_rsv_work);
ext4_fc_init_inode(&ei->vfs_inode);
spin_lock_init(&ei->i_fc_lock);
+ mmb_init(&ei->i_metadata_bhs, &ei->vfs_inode.i_data);
return &ei->vfs_inode;
}
@@ -1525,7 +1526,7 @@ void ext4_clear_inode(struct inode *inode)
{
ext4_fc_del(inode);
if (!EXT4_SB(inode->i_sb)->s_journal)
- invalidate_inode_buffers(inode);
+ mmb_invalidate_buffers(&EXT4_I(inode)->i_metadata_bhs);
clear_inode(inode);
ext4_discard_preallocations(inode);
ext4_es_remove_extent(inode, 0, EXT_MAX_BLOCKS);
--
2.51.0
^ permalink raw reply related
* [PATCH 39/41] fs: Drop mapping_metadata_bhs from address space
From: Jan Kara @ 2026-03-20 13:41 UTC (permalink / raw)
To: linux-fsdevel
Cc: linux-block, Christian Brauner, Al Viro, linux-ext4, Ted Tso,
Tigran A. Aivazian, David Sterba, OGAWA Hirofumi, Muchun Song,
Oscar Salvador, David Hildenbrand, linux-mm, linux-aio,
Benjamin LaHaise, Jan Kara
In-Reply-To: <20260320131728.6449-1-jack@suse.cz>
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 <jack@suse.cz>
---
fs/inode.c | 3 ---
include/linux/buffer_head.h | 28 ----------------------------
include/linux/fs.h | 1 -
3 files changed, 32 deletions(-)
diff --git a/fs/inode.c b/fs/inode.c
index 3874b933abdb..d5774e627a9c 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -276,7 +276,6 @@ int inode_init_always_gfp(struct super_block *sb, struct inode *inode, gfp_t gfp
mapping->a_ops = &empty_aops;
mapping->host = inode;
- mapping->i_metadata_bhs.mapping = mapping;
mapping->flags = 0;
mapping->wb_err = 0;
atomic_set(&mapping->i_mmap_writable, 0);
@@ -484,8 +483,6 @@ static void __address_space_init_once(struct address_space *mapping)
init_rwsem(&mapping->i_mmap_rwsem);
INIT_LIST_HEAD(&mapping->i_private_list);
spin_lock_init(&mapping->i_private_lock);
- spin_lock_init(&mapping->i_metadata_bhs.lock);
- INIT_LIST_HEAD(&mapping->i_metadata_bhs.list);
mapping->i_mmap = RB_ROOT_CACHED;
}
diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h
index 399277c679eb..74fcc9a03c32 100644
--- a/include/linux/buffer_head.h
+++ b/include/linux/buffer_head.h
@@ -207,29 +207,11 @@ 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 generic_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 generic_mmb_fsync_noflush(file, &file->f_mapping->i_metadata_bhs,
- start, end, datasync);
-}
int generic_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 generic_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)
@@ -538,14 +520,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_buffers(struct mapping_metadata_bhs *mmb);
int mmb_sync_buffers(struct mapping_metadata_bhs *mmb);
-static inline void invalidate_inode_buffers(struct inode *inode)
-{
- mmb_invalidate_buffers(&inode->i_data.i_metadata_bhs);
-}
-static inline int sync_mapping_buffers(struct address_space *mapping)
-{
- return mmb_sync_buffers(&mapping->i_metadata_bhs);
-}
void invalidate_bh_lrus(void);
void invalidate_bh_lrus_cpu(void);
bool has_bh_in_lru(int cpu, void *dummy);
@@ -556,8 +530,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_buffers(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; }
diff --git a/include/linux/fs.h b/include/linux/fs.h
index c4ab53ec36ab..d2122e1c9a3f 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -491,7 +491,6 @@ struct address_space {
errseq_t wb_err;
spinlock_t i_private_lock;
struct list_head i_private_list;
- struct mapping_metadata_bhs i_metadata_bhs;
struct rw_semaphore i_mmap_rwsem;
} __attribute__((aligned(sizeof(long)))) __randomize_layout;
/*
--
2.51.0
^ permalink raw reply related
* [PATCH 37/41] minix: Track metadata bhs in fs-private inode part
From: Jan Kara @ 2026-03-20 13:41 UTC (permalink / raw)
To: linux-fsdevel
Cc: linux-block, Christian Brauner, Al Viro, linux-ext4, Ted Tso,
Tigran A. Aivazian, David Sterba, OGAWA Hirofumi, Muchun Song,
Oscar Salvador, David Hildenbrand, linux-mm, linux-aio,
Benjamin LaHaise, Jan Kara
In-Reply-To: <20260320131728.6449-1-jack@suse.cz>
Track metadata bhs for an inode in fs-private part of the inode.
Signed-off-by: Jan Kara <jack@suse.cz>
---
fs/minix/dir.c | 2 +-
fs/minix/file.c | 10 +++++++++-
fs/minix/inode.c | 6 ++++--
fs/minix/itree_common.c | 11 +++++++----
fs/minix/minix.h | 3 +++
5 files changed, 24 insertions(+), 8 deletions(-)
diff --git a/fs/minix/dir.c b/fs/minix/dir.c
index a74d000327fa..361d26d87d2e 100644
--- a/fs/minix/dir.c
+++ b/fs/minix/dir.c
@@ -23,7 +23,7 @@ const struct file_operations minix_dir_operations = {
.llseek = generic_file_llseek,
.read = generic_read_dir,
.iterate_shared = minix_readdir,
- .fsync = generic_buffers_fsync,
+ .fsync = minix_fsync,
};
/*
diff --git a/fs/minix/file.c b/fs/minix/file.c
index 282b3cd1fea3..bc0be789343a 100644
--- a/fs/minix/file.c
+++ b/fs/minix/file.c
@@ -7,8 +7,16 @@
* minix regular file handling primitives
*/
+#include <linux/buffer_head.h>
#include "minix.h"
+int minix_fsync(struct file *file, loff_t start, loff_t end, int datasync)
+{
+ return generic_mmb_fsync(file,
+ &minix_i(file->f_mapping->host)->i_metadata_bhs,
+ start, end, datasync);
+}
+
/*
* We have mostly NULLs here: the current defaults are OK for
* the minix filesystem.
@@ -18,7 +26,7 @@ const struct file_operations minix_file_operations = {
.read_iter = generic_file_read_iter,
.write_iter = generic_file_write_iter,
.mmap_prepare = generic_file_mmap_prepare,
- .fsync = generic_buffers_fsync,
+ .fsync = minix_fsync,
.splice_read = filemap_splice_read,
};
diff --git a/fs/minix/inode.c b/fs/minix/inode.c
index ab7c06efb139..adba14628d1b 100644
--- a/fs/minix/inode.c
+++ b/fs/minix/inode.c
@@ -49,9 +49,9 @@ static void minix_evict_inode(struct inode *inode)
inode->i_size = 0;
minix_truncate(inode);
} else {
- sync_mapping_buffers(&inode->i_data);
+ mmb_sync_buffers(&minix_i(inode)->i_metadata_bhs);
}
- invalidate_inode_buffers(inode);
+ mmb_invalidate_buffers(&minix_i(inode)->i_metadata_bhs);
clear_inode(inode);
if (!inode->i_nlink)
minix_free_inode(inode);
@@ -85,6 +85,8 @@ static struct inode *minix_alloc_inode(struct super_block *sb)
ei = alloc_inode_sb(sb, minix_inode_cachep, GFP_KERNEL);
if (!ei)
return NULL;
+ mmb_init(&ei->i_metadata_bhs, &ei->vfs_inode.i_data);
+
return &ei->vfs_inode;
}
diff --git a/fs/minix/itree_common.c b/fs/minix/itree_common.c
index dad131e30c05..c3cd2c75af9c 100644
--- a/fs/minix/itree_common.c
+++ b/fs/minix/itree_common.c
@@ -98,7 +98,7 @@ static int alloc_branch(struct inode *inode,
*branch[n].p = branch[n].key;
set_buffer_uptodate(bh);
unlock_buffer(bh);
- mark_buffer_dirty_inode(bh, inode);
+ mmb_mark_buffer_dirty(bh, &minix_i(inode)->i_metadata_bhs);
parent = nr;
}
if (n == num)
@@ -135,7 +135,8 @@ static inline int splice_branch(struct inode *inode,
/* had we spliced it onto indirect block? */
if (where->bh)
- mark_buffer_dirty_inode(where->bh, inode);
+ mmb_mark_buffer_dirty(where->bh,
+ &minix_i(inode)->i_metadata_bhs);
mark_inode_dirty(inode);
return 0;
@@ -328,14 +329,16 @@ static inline void truncate (struct inode * inode)
if (partial == chain)
mark_inode_dirty(inode);
else
- mark_buffer_dirty_inode(partial->bh, inode);
+ mmb_mark_buffer_dirty(partial->bh,
+ &minix_i(inode)->i_metadata_bhs);
free_branches(inode, &nr, &nr+1, (chain+n-1) - partial);
}
/* Clear the ends of indirect blocks on the shared branch */
while (partial > chain) {
free_branches(inode, partial->p + 1, block_end(partial->bh),
(chain+n-1) - partial);
- mark_buffer_dirty_inode(partial->bh, inode);
+ mmb_mark_buffer_dirty(partial->bh,
+ &minix_i(inode)->i_metadata_bhs);
brelse (partial->bh);
partial--;
}
diff --git a/fs/minix/minix.h b/fs/minix/minix.h
index 7e1f652f16d3..f2025c9b5825 100644
--- a/fs/minix/minix.h
+++ b/fs/minix/minix.h
@@ -19,6 +19,7 @@ struct minix_inode_info {
__u16 i1_data[16];
__u32 i2_data[16];
} u;
+ struct mapping_metadata_bhs i_metadata_bhs;
struct inode vfs_inode;
};
@@ -57,6 +58,8 @@ unsigned long minix_count_free_blocks(struct super_block *sb);
int minix_getattr(struct mnt_idmap *, const struct path *,
struct kstat *, u32, unsigned int);
int minix_prepare_chunk(struct folio *folio, loff_t pos, unsigned len);
+struct mapping_metadata_bhs *minix_get_metadata_bhs(struct inode *inode);
+int minix_fsync(struct file *file, loff_t start, loff_t end, int datasync);
extern void V1_minix_truncate(struct inode *);
extern void V2_minix_truncate(struct inode *);
--
2.51.0
^ permalink raw reply related
* [PATCH 40/41] fs: Drop i_private_list from address_space
From: Jan Kara @ 2026-03-20 13:41 UTC (permalink / raw)
To: linux-fsdevel
Cc: linux-block, Christian Brauner, Al Viro, linux-ext4, Ted Tso,
Tigran A. Aivazian, David Sterba, OGAWA Hirofumi, Muchun Song,
Oscar Salvador, David Hildenbrand, linux-mm, linux-aio,
Benjamin LaHaise, Jan Kara
In-Reply-To: <20260320131728.6449-1-jack@suse.cz>
Nobody is using i_private_list anymore. Remove it.
Signed-off-by: Jan Kara <jack@suse.cz>
---
fs/inode.c | 2 --
include/linux/fs.h | 2 --
2 files changed, 4 deletions(-)
diff --git a/fs/inode.c b/fs/inode.c
index d5774e627a9c..a8f019078fab 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -481,7 +481,6 @@ static void __address_space_init_once(struct address_space *mapping)
{
xa_init_flags(&mapping->i_pages, XA_FLAGS_LOCK_IRQ | XA_FLAGS_ACCOUNT);
init_rwsem(&mapping->i_mmap_rwsem);
- INIT_LIST_HEAD(&mapping->i_private_list);
spin_lock_init(&mapping->i_private_lock);
mapping->i_mmap = RB_ROOT_CACHED;
}
@@ -795,7 +794,6 @@ void clear_inode(struct inode *inode)
* nor even WARN_ON(!mapping_empty).
*/
xa_unlock_irq(&inode->i_data.i_pages);
- BUG_ON(!list_empty(&inode->i_data.i_private_list));
BUG_ON(!(inode_state_read_once(inode) & I_FREEING));
BUG_ON(inode_state_read_once(inode) & I_CLEAR);
BUG_ON(!list_empty(&inode->i_wb_list));
diff --git a/include/linux/fs.h b/include/linux/fs.h
index d2122e1c9a3f..caa9203ed213 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -471,7 +471,6 @@ struct mapping_metadata_bhs {
* @flags: Error bits and flags (AS_*).
* @wb_err: The most recent error which has occurred.
* @i_private_lock: For use by the owner of the address_space.
- * @i_private_list: For use by the owner of the address_space.
*/
struct address_space {
struct inode *host;
@@ -490,7 +489,6 @@ struct address_space {
unsigned long flags;
errseq_t wb_err;
spinlock_t i_private_lock;
- struct list_head i_private_list;
struct rw_semaphore i_mmap_rwsem;
} __attribute__((aligned(sizeof(long)))) __randomize_layout;
/*
--
2.51.0
^ permalink raw reply related
* [PATCH 36/41] udf: Track metadata bhs in fs-private inode part
From: Jan Kara @ 2026-03-20 13:41 UTC (permalink / raw)
To: linux-fsdevel
Cc: linux-block, Christian Brauner, Al Viro, linux-ext4, Ted Tso,
Tigran A. Aivazian, David Sterba, OGAWA Hirofumi, Muchun Song,
Oscar Salvador, David Hildenbrand, linux-mm, linux-aio,
Benjamin LaHaise, Jan Kara
In-Reply-To: <20260320131728.6449-1-jack@suse.cz>
Track metadata bhs for an inode in fs-private part of the inode.
Signed-off-by: Jan Kara <jack@suse.cz>
---
fs/udf/dir.c | 2 +-
fs/udf/directory.c | 5 +++--
fs/udf/file.c | 9 ++++++++-
fs/udf/inode.c | 16 ++++++++--------
fs/udf/namei.c | 2 +-
fs/udf/super.c | 1 +
fs/udf/truncate.c | 2 +-
fs/udf/udf_i.h | 1 +
fs/udf/udfdecl.h | 1 +
9 files changed, 25 insertions(+), 14 deletions(-)
diff --git a/fs/udf/dir.c b/fs/udf/dir.c
index a1705aedac46..ebc9f6a379fe 100644
--- a/fs/udf/dir.c
+++ b/fs/udf/dir.c
@@ -157,6 +157,6 @@ const struct file_operations udf_dir_operations = {
.read = generic_read_dir,
.iterate_shared = udf_readdir,
.unlocked_ioctl = udf_ioctl,
- .fsync = generic_buffers_fsync,
+ .fsync = udf_fsync,
.setlease = generic_setlease,
};
diff --git a/fs/udf/directory.c b/fs/udf/directory.c
index 632453aa3893..83edd04ca6fa 100644
--- a/fs/udf/directory.c
+++ b/fs/udf/directory.c
@@ -430,9 +430,10 @@ void udf_fiiter_write_fi(struct udf_fileident_iter *iter, uint8_t *impuse)
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
mark_inode_dirty(iter->dir);
} else {
- mark_buffer_dirty_inode(iter->bh[0], iter->dir);
+ mmb_mark_buffer_dirty(iter->bh[0], &iinfo->i_metadata_bhs);
if (iter->bh[1])
- mark_buffer_dirty_inode(iter->bh[1], iter->dir);
+ mmb_mark_buffer_dirty(iter->bh[1],
+ &iinfo->i_metadata_bhs);
}
inode_inc_iversion(iter->dir);
}
diff --git a/fs/udf/file.c b/fs/udf/file.c
index 627b07320d06..bce3667fa2d4 100644
--- a/fs/udf/file.c
+++ b/fs/udf/file.c
@@ -198,6 +198,13 @@ static int udf_file_mmap(struct file *file, struct vm_area_struct *vma)
return 0;
}
+int udf_fsync(struct file *file, loff_t start, loff_t end, int datasync)
+{
+ return generic_mmb_fsync(file,
+ &UDF_I(file->f_mapping->host)->i_metadata_bhs,
+ start, end, datasync);
+}
+
const struct file_operations udf_file_operations = {
.read_iter = generic_file_read_iter,
.unlocked_ioctl = udf_ioctl,
@@ -205,7 +212,7 @@ const struct file_operations udf_file_operations = {
.mmap = udf_file_mmap,
.write_iter = udf_file_write_iter,
.release = udf_release_file,
- .fsync = generic_buffers_fsync,
+ .fsync = udf_fsync,
.splice_read = filemap_splice_read,
.splice_write = iter_file_splice_write,
.llseek = generic_file_llseek,
diff --git a/fs/udf/inode.c b/fs/udf/inode.c
index 739b190ca4e9..6b6b0116cf90 100644
--- a/fs/udf/inode.c
+++ b/fs/udf/inode.c
@@ -155,8 +155,8 @@ void udf_evict_inode(struct inode *inode)
}
truncate_inode_pages_final(&inode->i_data);
if (!want_delete)
- sync_mapping_buffers(&inode->i_data);
- invalidate_inode_buffers(inode);
+ mmb_sync_buffers(&iinfo->i_metadata_bhs);
+ mmb_invalidate_buffers(&iinfo->i_metadata_bhs);
clear_inode(inode);
kfree(iinfo->i_data);
iinfo->i_data = NULL;
@@ -1263,7 +1263,7 @@ struct buffer_head *udf_bread(struct inode *inode, udf_pblk_t block,
memset(bh->b_data, 0x00, inode->i_sb->s_blocksize);
set_buffer_uptodate(bh);
unlock_buffer(bh);
- mark_buffer_dirty_inode(bh, inode);
+ mmb_mark_buffer_dirty(bh, &UDF_I(inode)->i_metadata_bhs);
return bh;
}
@@ -2011,7 +2011,7 @@ int udf_setup_indirect_aext(struct inode *inode, udf_pblk_t block,
memset(bh->b_data, 0x00, sb->s_blocksize);
set_buffer_uptodate(bh);
unlock_buffer(bh);
- mark_buffer_dirty_inode(bh, inode);
+ mmb_mark_buffer_dirty(bh, &UDF_I(inode)->i_metadata_bhs);
aed = (struct allocExtDesc *)(bh->b_data);
if (!UDF_QUERY_FLAG(sb, UDF_FLAG_STRICT)) {
@@ -2106,7 +2106,7 @@ int __udf_add_aext(struct inode *inode, struct extent_position *epos,
else
udf_update_tag(epos->bh->b_data,
sizeof(struct allocExtDesc));
- mark_buffer_dirty_inode(epos->bh, inode);
+ mmb_mark_buffer_dirty(epos->bh, &iinfo->i_metadata_bhs);
}
return 0;
@@ -2190,7 +2190,7 @@ void udf_write_aext(struct inode *inode, struct extent_position *epos,
le32_to_cpu(aed->lengthAllocDescs) +
sizeof(struct allocExtDesc));
}
- mark_buffer_dirty_inode(epos->bh, inode);
+ mmb_mark_buffer_dirty(epos->bh, &iinfo->i_metadata_bhs);
} else {
mark_inode_dirty(inode);
}
@@ -2398,7 +2398,7 @@ int8_t udf_delete_aext(struct inode *inode, struct extent_position epos)
else
udf_update_tag(oepos.bh->b_data,
sizeof(struct allocExtDesc));
- mark_buffer_dirty_inode(oepos.bh, inode);
+ mmb_mark_buffer_dirty(oepos.bh, &iinfo->i_metadata_bhs);
}
} else {
udf_write_aext(inode, &oepos, &eloc, elen, 1);
@@ -2415,7 +2415,7 @@ int8_t udf_delete_aext(struct inode *inode, struct extent_position epos)
else
udf_update_tag(oepos.bh->b_data,
sizeof(struct allocExtDesc));
- mark_buffer_dirty_inode(oepos.bh, inode);
+ mmb_mark_buffer_dirty(oepos.bh, &iinfo->i_metadata_bhs);
}
}
diff --git a/fs/udf/namei.c b/fs/udf/namei.c
index 5f2e9a892bff..4ef2ff014170 100644
--- a/fs/udf/namei.c
+++ b/fs/udf/namei.c
@@ -638,7 +638,7 @@ static int udf_symlink(struct mnt_idmap *idmap, struct inode *dir,
memset(epos.bh->b_data, 0x00, bsize);
set_buffer_uptodate(epos.bh);
unlock_buffer(epos.bh);
- mark_buffer_dirty_inode(epos.bh, inode);
+ mmb_mark_buffer_dirty(epos.bh, &iinfo->i_metadata_bhs);
ea = epos.bh->b_data + udf_ext0_offset(inode);
} else
ea = iinfo->i_data + iinfo->i_lenEAttr;
diff --git a/fs/udf/super.c b/fs/udf/super.c
index 27f463fd1d89..e02775007c46 100644
--- a/fs/udf/super.c
+++ b/fs/udf/super.c
@@ -166,6 +166,7 @@ static struct inode *udf_alloc_inode(struct super_block *sb)
ei->cached_extent.lstart = -1;
spin_lock_init(&ei->i_extent_cache_lock);
inode_set_iversion(&ei->vfs_inode, 1);
+ mmb_init(&ei->i_metadata_bhs, &ei->vfs_inode.i_data);
return &ei->vfs_inode;
}
diff --git a/fs/udf/truncate.c b/fs/udf/truncate.c
index b4071c9cf8c9..41b2bfd30449 100644
--- a/fs/udf/truncate.c
+++ b/fs/udf/truncate.c
@@ -186,7 +186,7 @@ static void udf_update_alloc_ext_desc(struct inode *inode,
len += lenalloc;
udf_update_tag(epos->bh->b_data, len);
- mark_buffer_dirty_inode(epos->bh, inode);
+ mmb_mark_buffer_dirty(epos->bh, &UDF_I(inode)->i_metadata_bhs);
}
/*
diff --git a/fs/udf/udf_i.h b/fs/udf/udf_i.h
index 312b7c9ef10e..fdaa88c49c2b 100644
--- a/fs/udf/udf_i.h
+++ b/fs/udf/udf_i.h
@@ -50,6 +50,7 @@ struct udf_inode_info {
struct kernel_lb_addr i_locStreamdir;
__u64 i_lenStreams;
struct rw_semaphore i_data_sem;
+ struct mapping_metadata_bhs i_metadata_bhs;
struct udf_ext_cache cached_extent;
/* Spinlock for protecting extent cache */
spinlock_t i_extent_cache_lock;
diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h
index d159f20d61e8..6d951e05c004 100644
--- a/fs/udf/udfdecl.h
+++ b/fs/udf/udfdecl.h
@@ -137,6 +137,7 @@ static inline unsigned int udf_dir_entry_len(struct fileIdentDesc *cfi)
/* file.c */
extern long udf_ioctl(struct file *, unsigned int, unsigned long);
+int udf_fsync(struct file *file, loff_t start, loff_t end, int datasync);
/* inode.c */
extern struct inode *__udf_iget(struct super_block *, struct kernel_lb_addr *,
--
2.51.0
^ permalink raw reply related
* [PATCH 41/41] fs: Unify generic_file_fsync() with mmb methods
From: Jan Kara @ 2026-03-20 13:41 UTC (permalink / raw)
To: linux-fsdevel
Cc: linux-block, Christian Brauner, Al Viro, linux-ext4, Ted Tso,
Tigran A. Aivazian, David Sterba, OGAWA Hirofumi, Muchun Song,
Oscar Salvador, David Hildenbrand, linux-mm, linux-aio,
Benjamin LaHaise, Jan Kara
In-Reply-To: <20260320131728.6449-1-jack@suse.cz>
__generic_file_fsync() is practically identical to
generic_mmb_fsync_noflush() with one subtle difference:
1) __generic_file_fsync() takes inode lock when calling writing out the
inode.
2) generic_mmb_fsync_noflush() calls mmb_sync_buffers().
Taking inode lock when writing out the inode seems pointless in
particular because there are lots of places (most notably sync(2) path)
that don't do that so hardly anything can depend on it. When NULL is
passed to generic_mmb_fsync_noflush(), mmb_sync_buffers() is not called
so that difference is not a problem.
So let's remove __generic_file_fsync() and use
generic_mmb_fsync_noflush() instead to reduce code duplication. Arguably
this leaks a bit of buffer_head knowledge into fs/libfs.c which is not
great but avoiding the duplication seems worth it.
Signed-off-by: Jan Kara <jack@suse.cz>
---
fs/buffer.c | 74 -------------------------------------
fs/exfat/file.c | 2 +-
fs/libfs.c | 57 ++++++++++++++++------------
include/linux/buffer_head.h | 5 ---
include/linux/fs.h | 12 +++++-
5 files changed, 45 insertions(+), 105 deletions(-)
diff --git a/fs/buffer.c b/fs/buffer.c
index 43aca5b7969f..591aed740601 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -621,80 +621,6 @@ int mmb_sync_buffers(struct mapping_metadata_bhs *mmb)
}
EXPORT_SYMBOL(mmb_sync_buffers);
-/**
- * generic_mmb_fsync_noflush - generic buffer fsync implementation
- * for simple filesystems with no inode lock
- *
- * @file: file to synchronize
- * @mmb: list of metadata bhs to flush
- * @start: start offset in bytes
- * @end: end offset in bytes (inclusive)
- * @datasync: only synchronize essential metadata if true
- *
- * This is a generic implementation of the fsync method for simple
- * filesystems which track all non-inode metadata in the buffers list
- * hanging off the address_space structure.
- */
-int generic_mmb_fsync_noflush(struct file *file,
- struct mapping_metadata_bhs *mmb,
- loff_t start, loff_t end, bool datasync)
-{
- struct inode *inode = file->f_mapping->host;
- int err;
- int ret = 0;
-
- err = file_write_and_wait_range(file, start, end);
- if (err)
- return err;
-
- if (mmb)
- ret = mmb_sync_buffers(mmb);
- if (!(inode_state_read_once(inode) & I_DIRTY_ALL))
- goto out;
- if (datasync && !(inode_state_read_once(inode) & I_DIRTY_DATASYNC))
- goto out;
-
- err = sync_inode_metadata(inode, 1);
- if (ret == 0)
- ret = err;
-
-out:
- /* check and advance again to catch errors after syncing out buffers */
- err = file_check_and_advance_wb_err(file);
- if (ret == 0)
- ret = err;
- return ret;
-}
-EXPORT_SYMBOL(generic_mmb_fsync_noflush);
-
-/**
- * generic_mmb_fsync - generic buffer fsync implementation
- * for simple filesystems with no inode lock
- *
- * @file: file to synchronize
- * @mmb: list of metadata bhs to flush
- * @start: start offset in bytes
- * @end: end offset in bytes (inclusive)
- * @datasync: only synchronize essential metadata if true
- *
- * This is a generic implementation of the fsync method for simple
- * filesystems which track all non-inode metadata in the buffers list
- * hanging off the address_space structure. This also makes sure that
- * a device cache flush operation is called at the end.
- */
-int generic_mmb_fsync(struct file *file, struct mapping_metadata_bhs *mmb,
- loff_t start, loff_t end, bool datasync)
-{
- struct inode *inode = file->f_mapping->host;
- int ret;
-
- ret = generic_mmb_fsync_noflush(file, mmb, start, end, datasync);
- if (!ret)
- ret = blkdev_issue_flush(inode->i_sb->s_bdev);
- return ret;
-}
-EXPORT_SYMBOL(generic_mmb_fsync);
-
/*
* Called when we've recently written block `bblock', and it is known that
* `bblock' was for a buffer_boundary() buffer. This means that the block at
diff --git a/fs/exfat/file.c b/fs/exfat/file.c
index 90cd540afeaa..fe6eb391eb4e 100644
--- a/fs/exfat/file.c
+++ b/fs/exfat/file.c
@@ -577,7 +577,7 @@ int exfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync)
if (unlikely(exfat_forced_shutdown(inode->i_sb)))
return -EIO;
- err = __generic_file_fsync(filp, start, end, datasync);
+ err = generic_mmb_fsync_noflush(filp, NULL, start, end, datasync);
if (err)
return err;
diff --git a/fs/libfs.c b/fs/libfs.c
index 548e119668df..7c1d78862e39 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -18,7 +18,7 @@
#include <linux/exportfs.h>
#include <linux/iversion.h>
#include <linux/writeback.h>
-#include <linux/buffer_head.h> /* sync_mapping_buffers */
+#include <linux/buffer_head.h> /* mmb_sync_buffers() */
#include <linux/fs_context.h>
#include <linux/pseudo_fs.h>
#include <linux/fsnotify.h>
@@ -1539,19 +1539,22 @@ struct dentry *generic_fh_to_parent(struct super_block *sb, struct fid *fid,
EXPORT_SYMBOL_GPL(generic_fh_to_parent);
/**
- * __generic_file_fsync - generic fsync implementation for simple filesystems
+ * generic_mmb_fsync_noflush - generic buffer fsync implementation
+ * for simple filesystems with no inode lock
*
- * @file: file to synchronize
- * @start: start offset in bytes
- * @end: end offset in bytes (inclusive)
- * @datasync: only synchronize essential metadata if true
+ * @file: file to synchronize
+ * @mmb: list of metadata bhs to flush
+ * @start: start offset in bytes
+ * @end: end offset in bytes (inclusive)
+ * @datasync: only synchronize essential metadata if true
*
* This is a generic implementation of the fsync method for simple
* filesystems which track all non-inode metadata in the buffers list
* hanging off the address_space structure.
*/
-int __generic_file_fsync(struct file *file, loff_t start, loff_t end,
- int datasync)
+int generic_mmb_fsync_noflush(struct file *file,
+ struct mapping_metadata_bhs *mmb,
+ loff_t start, loff_t end, bool datasync)
{
struct inode *inode = file->f_mapping->host;
int err;
@@ -1561,45 +1564,53 @@ int __generic_file_fsync(struct file *file, loff_t start, loff_t end,
if (err)
return err;
- inode_lock(inode);
+ if (mmb)
+ ret = mmb_sync_buffers(mmb);
if (!(inode_state_read_once(inode) & I_DIRTY_ALL))
goto out;
if (datasync && !(inode_state_read_once(inode) & I_DIRTY_DATASYNC))
goto out;
- ret = sync_inode_metadata(inode, 1);
+ err = sync_inode_metadata(inode, 1);
+ if (ret == 0)
+ ret = err;
+
out:
- inode_unlock(inode);
/* check and advance again to catch errors after syncing out buffers */
err = file_check_and_advance_wb_err(file);
if (ret == 0)
ret = err;
return ret;
}
-EXPORT_SYMBOL(__generic_file_fsync);
+EXPORT_SYMBOL(generic_mmb_fsync_noflush);
/**
- * generic_file_fsync - generic fsync implementation for simple filesystems
- * with flush
+ * generic_mmb_fsync - generic buffer fsync implementation
+ * for simple filesystems with no inode lock
+ *
* @file: file to synchronize
+ * @mmb: list of metadata bhs to flush
* @start: start offset in bytes
* @end: end offset in bytes (inclusive)
* @datasync: only synchronize essential metadata if true
*
+ * This is a generic implementation of the fsync method for simple
+ * filesystems which track all non-inode metadata in the buffers list
+ * hanging off the address_space structure. This also makes sure that
+ * a device cache flush operation is called at the end.
*/
-
-int generic_file_fsync(struct file *file, loff_t start, loff_t end,
- int datasync)
+int generic_mmb_fsync(struct file *file, struct mapping_metadata_bhs *mmb,
+ loff_t start, loff_t end, bool datasync)
{
struct inode *inode = file->f_mapping->host;
- int err;
+ int ret;
- err = __generic_file_fsync(file, start, end, datasync);
- if (err)
- return err;
- return blkdev_issue_flush(inode->i_sb->s_bdev);
+ ret = generic_mmb_fsync_noflush(file, mmb, start, end, datasync);
+ if (!ret)
+ ret = blkdev_issue_flush(inode->i_sb->s_bdev);
+ return ret;
}
-EXPORT_SYMBOL(generic_file_fsync);
+EXPORT_SYMBOL(generic_mmb_fsync);
/**
* generic_check_addressable - Check addressability of file system
diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h
index 74fcc9a03c32..f003a1937826 100644
--- a/include/linux/buffer_head.h
+++ b/include/linux/buffer_head.h
@@ -207,11 +207,6 @@ 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);
-int generic_mmb_fsync_noflush(struct file *file,
- struct mapping_metadata_bhs *mmb,
- loff_t start, loff_t end, bool datasync);
-int generic_mmb_fsync(struct file *file, struct mapping_metadata_bhs *mmb,
- loff_t start, loff_t end, bool 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)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index caa9203ed213..32178e53d448 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -3298,8 +3298,16 @@ void simple_offset_destroy(struct offset_ctx *octx);
extern const struct file_operations simple_offset_dir_operations;
-extern int __generic_file_fsync(struct file *, loff_t, loff_t, int);
-extern int generic_file_fsync(struct file *, loff_t, loff_t, int);
+int generic_mmb_fsync_noflush(struct file *file,
+ struct mapping_metadata_bhs *mmb,
+ loff_t start, loff_t end, bool datasync);
+int generic_mmb_fsync(struct file *file, struct mapping_metadata_bhs *mmb,
+ loff_t start, loff_t end, bool datasync);
+static inline int generic_file_fsync(struct file *file,
+ loff_t start, loff_t end, int datasync)
+{
+ return generic_mmb_fsync(file, NULL, start, end, datasync);
+}
extern int generic_check_addressable(unsigned, u64);
--
2.51.0
^ permalink raw reply related
* Re: [PATCHBLIZZARD v7] fuse/libfuse/e2fsprogs: containerize ext4 for safer operation
From: Joanne Koong @ 2026-03-20 17:04 UTC (permalink / raw)
To: Darrick J. Wong
Cc: Demi Marie Obenour, linux-fsdevel, bpf, linux-ext4,
Miklos Szeredi, Bernd Schubert, Theodore Ts'o, Neal Gompa,
Amir Goldstein, Christian Brauner, Jeff Layton, John
In-Reply-To: <20260319160836.GC6004@frogsfrogsfrogs>
On Thu, Mar 19, 2026 at 9:08 AM Darrick J. Wong <djwong@kernel.org> wrote:
>
> On Thu, Mar 19, 2026 at 03:28:21AM -0400, Demi Marie Obenour wrote:
> > On 3/18/26 17:31, Darrick J. Wong wrote:
> > > On Mon, Mar 16, 2026 at 08:20:29PM -0400, Demi Marie Obenour wrote:
> > >> On 3/16/26 19:41, Darrick J. Wong wrote:
> > >>> On Mon, Mar 16, 2026 at 04:08:55PM -0700, Joanne Koong wrote:
> > >>>> On Mon, Mar 16, 2026 at 11:04 AM Darrick J. Wong <djwong@kernel.org> wrote:
> > >>>>>
> > >>>>> On Mon, Mar 16, 2026 at 10:56:21AM -0700, Joanne Koong wrote:
> > >>>>>> On Mon, Feb 23, 2026 at 2:46 PM Darrick J. Wong <djwong@kernel.org> wrote:
> > >>>>>>>
> > >>>>>>> There are some warts remaining:
> > >>>>>>>
> > >>>>>>> a. I would like to continue the discussion about how the design review
> > >>>>>>> of this code should be structured, and how might I go about creating
> > >>>>>>> new userspace filesystem servers -- lightweight new ones based off
> > >>>>>>> the existing userspace tools? Or by merging lklfuse?
> > >>>>>>
> > >>>>>> What do you mean by "merging lklfuse"?
> > >>>>>
> > >>>>> Merging the lklfuse project into upstream Linux, which involves running
> > >>>>> the whole kit and caboodle through our review process, and then fixing
> > >>>>
> > >>>> Gotcha, so it would basically be having to port this arch/lkl
> > >>>> directory [1] into the linux tree
> > >>>
> > >>> Right.
> > >>>
> > >>>>> user-mode-linux to work anywhere other than x86.
> > >>>>
> > >>>> Are lklfuse and user-mode-linux (UML) two separate things or is
> > >>>> lklfuse dependent on user-mode-linux?
> > >>>
> > >>> I was under the impression that lklfuse uses UML. Given the weird
> > >>> things in arch/lkl/Kconfig:
> > >>>
> > >>> config 64BIT
> > >>> bool "64bit kernel"
> > >>> default y if OUTPUT_FORMAT = "pe-x86-64"
> > >>> default $(success,$(srctree)/arch/lkl/scripts/cc-objdump-file-format.sh|grep -q '^elf64-') if OUTPUT_FORMAT != "pe-x86-64"
> > >>>
> > >>> I was kinda guessing x86_64 was the primary target of the developers?
> > >>>
> > >>> /me notes that he's now looked into libguestfs per Demi Marie's comments
> > >>> and some curiosity on the part of ngompa and i>
> > >>>
> > >>> Whatever it is that libguestfs does to stand up unprivileged fs mounts
> > >>> also could fit this bill. It's *really* slow to start because it takes
> > >>> the booted kernel, creates a largeish initramfs, boots that combo via
> > >>> libvirt, and then fires up a fuse server to talk to the vm kernel.
> > >>>
> > >>> I think all you'd have to do is change libguestfs to start the VM and
> > >>> run the fuse server inside a systemd container instead of directly from
> > >>> the CLI.
> > >>
> > >> The feedback I have gotten from ngompa is that libguestfs is just
> > >> too slow for distros to use it to mount stuff.
> > >
> > > Yes, libguestfs is /verrrry/ slow to start up.
> > >
> > >>>>>> Could you explain what the limitations of lklfuse are compared to the
> > >>>>>> fuse iomap approach in this patchset?
> > >>>>>
> > >>>>> The ones I know about are:
> > >>>>>
> > >>>>> 1> There's no support for vmapped kernel memory in UML mode, so anyone
> > >>>>> who requires a large contiguous memory buffer cannot assemble them out
> > >>>>> of "physical" pages. This has been a stumbling block for XFS in the
> > >>>>> past.
> > >>>>>
> > >>>>> 2> LKLFUSE still uses the classic fuse IO paths, which means that at
> > >>>>> best you can directio the IO through the lklfuse kernel. At worst you
> > >>>>> have to use the pagecache inside the lklfuse kernel, which is very
> > >>>>> wasteful.
> > >>>>
> > >>>> For the security / isolation use cases you've described, is
> > >>>> near-native performance a hard requirement?
> > >>>
> > >>> Not a hard requirement, just a means to convince people that they can
> > >>> choose containment without completely collapsing performance.
> > >>>
> > >>>> As I understand it, the main use cases of this will be for mounting
> > >>>> untrusted disk images and CI/filesystem testing, or are there broader
> > >>>> use cases beyond this?
> > >>>
> > >>> That covers nearly all of it.
> > >>
> > >> It's worth noting that on ChromeOS and Android, the only trusted
> > >> disk images are those that are read-only and protected by dm-verity.
> > >> *Every* writable image is considered untrusted.
> > >>
> > >> I don't know if doing a full fsck at each boot is considered
> > >> acceptable, but I suspect it would slow boot far too much.
> > >
> > > Not to mention that an attacker who gained control of the boot process
> > > could inject malicious filesystem metadata after fsck completes
> > > successfully but before the kernel mount occurs.
> > >
> > >> Yes, Google ought to be paying for the kernel changes to fix this mess.
> > >>
> > >>>>> 3> lklfuse hasn't been updated since 6.6.
> > >>>>
> > >>>> Gotcha. So if I'm understanding it correctly, the pros/cons come down to:
> > >>>> lklfuse pros:
> > >>>> - (arguably) easier setup cost. once it's setup (assuming it's
> > >>>> possible to add support for the vmapped kernel memory thing you
> > >>>> mentioned above), it'll automatically work for every filesystem vs.
> > >>>> having to implement a fuse-iomap server for every filesystem
> > >>>
> > >>> Or even a good non-iomap fuse server for every filesystem. Admittedly
> > >>> the weak part of fuse4fs is that libext2fs is not as robust as the
> > >>> kernel is.
> > >>>
> > >>>> - easier to maintain vs. having to maintain each filesystem's
> > >>>> userspace server implementation
> > >>>
> > >>> Yeah.
> > >>>
> > >>>> lklfuse cons:
> > >>>> - worse (not sure by how much) performance
> > >>>
> > >>> Probably a lot, because now you have to run a full IO stack all the way
> > >>> through lklfuse.
> > >>
> > >> How much is "a lot"? Is it "this is only useful for non-interactive
> > >> overnight backups", "you will notice this in benchmarks but it's okay
> > >> for normal use", or somewhere in between?
> > >
> > > Startup is painfully slow. Normal operation isn't noticeably bad, but I
> > > didn't bother doing any performance comparisons.
For the CI/filesystem testing use case, could fork() help amortize
lklfuse's slow startup time? eg start lklfuse + pay LKL initialization
cost once, fork for each test, and each child mounts its own test
image?
> > >
> > >> Could lklfuse and iomap be combined?
> > >
> > > Probably, though you'd have to find a way to route the FUSE_IOMAP_*
> > > requests to a filesystem driver. That's upside-down of the current
> > > iomap model where filesystems have to opt into using iomap on a
> > > per-IO-path basis, and then iomap calls the filesystem to find mappings.
> >
> > If it does get done it would be awesome. I don't think I'll be able to
> > contribute, though.
>
> I wonder if one could export a (pnfs) layout from the lklfuse kernel to
> the real one, that's <cough> where struct iomap came from. A huge
> downside to that solution is that layouts don't support out of place
> writes because pnfs doesn't support out of place writes.
>
> > >>>> - once it's merged into the kernel, we can't choose to not
> > >>>> maintain/support it in the future
> > >>>
> > >>> Correct.
> > >>>
> > >>>> Am I understanding this correctly?
> > >>>>
> > >>>> In my opinion, if near-native performance is not a hard requirement,
> > >>>> it seems like less pain overall to go with lklfuse. lklfuse seems a
> > >>>> lot easier to maintain and I'm not sure if some complexities like
> > >>>> btrfs's copy-on-write could be handled properly with fuse-iomap.
> > >>>
> > >>> btrfs cow can be done with iomap, at least on the directio end. It's
> > >>> the other features like fsverity/fscrypt/data checksumming that aren't
> > >>> currently supported by iomap.
> > >>
> > >> Pretty much everyone on btrfs uses data checksumming.
> > >>
> > >>>> What are your thoughts on this?
> > >>>
> > >>> "Gee, what if I could simplify most of my own work out of existence?"
> > >>
> > >> What is that work?
> > >
> > > Everything I've put out since the end of online fsck for xfs.
> >
> > Is pretty much all of that work either on better FUSE performance or
> > fixes for problems found by fuzzers?
>
> Mostly the iomap parts of fuse-iomap. It's a huge complication to add
> to the already confusing fuse codebase.
imo if you did end up going the lklfuse route, I think it'd still be
useful to have the generic iomap infrastructure pieces of your
fuse-iomap patchblizzard added, for future new filesystem
implementations that can provide extent mappings to get near-native IO
performance.
Thanks,
Joanne
>
> --D
^ permalink raw reply
* Re: [PATCHBLIZZARD v7] fuse/libfuse/e2fsprogs: containerize ext4 for safer operation
From: Darrick J. Wong @ 2026-03-20 20:31 UTC (permalink / raw)
To: Joanne Koong
Cc: Demi Marie Obenour, linux-fsdevel, bpf, linux-ext4,
Miklos Szeredi, Bernd Schubert, Theodore Ts'o, Neal Gompa,
Amir Goldstein, Christian Brauner, Jeff Layton, John
In-Reply-To: <CAJnrk1Zpxv1CyUn2sbN2j73M57F8r4foyDQQ0Df_g0Ev+yx-Qg@mail.gmail.com>
On Fri, Mar 20, 2026 at 10:04:29AM -0700, Joanne Koong wrote:
<snip>
> > > >>>> - easier to maintain vs. having to maintain each filesystem's
> > > >>>> userspace server implementation
> > > >>>
> > > >>> Yeah.
> > > >>>
> > > >>>> lklfuse cons:
> > > >>>> - worse (not sure by how much) performance
> > > >>>
> > > >>> Probably a lot, because now you have to run a full IO stack all the way
> > > >>> through lklfuse.
> > > >>
> > > >> How much is "a lot"? Is it "this is only useful for non-interactive
> > > >> overnight backups", "you will notice this in benchmarks but it's okay
> > > >> for normal use", or somewhere in between?
> > > >
> > > > Startup is painfully slow. Normal operation isn't noticeably bad, but I
> > > > didn't bother doing any performance comparisons.
>
> For the CI/filesystem testing use case, could fork() help amortize
> lklfuse's slow startup time? eg start lklfuse + pay LKL initialization
> cost once, fork for each test, and each child mounts its own test
> image?
I suppose you could prefork like that (with some difficulty) if you had
a means to convey the device, fstype, and mount options to each new
child as a mount request comes in.
Though if you had a way to do that, then you might be better off
shipping a canned kernel + hibernate image which would "unhibernate"
quickly, discover the fs parameters, and immediately engage the fuse
server.
> > > >
> > > >> Could lklfuse and iomap be combined?
> > > >
> > > > Probably, though you'd have to find a way to route the FUSE_IOMAP_*
> > > > requests to a filesystem driver. That's upside-down of the current
> > > > iomap model where filesystems have to opt into using iomap on a
> > > > per-IO-path basis, and then iomap calls the filesystem to find mappings.
> > >
> > > If it does get done it would be awesome. I don't think I'll be able to
> > > contribute, though.
> >
> > I wonder if one could export a (pnfs) layout from the lklfuse kernel to
> > the real one, that's <cough> where struct iomap came from. A huge
> > downside to that solution is that layouts don't support out of place
> > writes because pnfs doesn't support out of place writes.
> >
> > > >>>> - once it's merged into the kernel, we can't choose to not
> > > >>>> maintain/support it in the future
> > > >>>
> > > >>> Correct.
> > > >>>
> > > >>>> Am I understanding this correctly?
> > > >>>>
> > > >>>> In my opinion, if near-native performance is not a hard requirement,
> > > >>>> it seems like less pain overall to go with lklfuse. lklfuse seems a
> > > >>>> lot easier to maintain and I'm not sure if some complexities like
> > > >>>> btrfs's copy-on-write could be handled properly with fuse-iomap.
> > > >>>
> > > >>> btrfs cow can be done with iomap, at least on the directio end. It's
> > > >>> the other features like fsverity/fscrypt/data checksumming that aren't
> > > >>> currently supported by iomap.
> > > >>
> > > >> Pretty much everyone on btrfs uses data checksumming.
> > > >>
> > > >>>> What are your thoughts on this?
> > > >>>
> > > >>> "Gee, what if I could simplify most of my own work out of existence?"
> > > >>
> > > >> What is that work?
> > > >
> > > > Everything I've put out since the end of online fsck for xfs.
> > >
> > > Is pretty much all of that work either on better FUSE performance or
> > > fixes for problems found by fuzzers?
> >
> > Mostly the iomap parts of fuse-iomap. It's a huge complication to add
> > to the already confusing fuse codebase.
>
> imo if you did end up going the lklfuse route, I think it'd still be
> useful to have the generic iomap infrastructure pieces of your
> fuse-iomap patchblizzard added, for future new filesystem
> implementations that can provide extent mappings to get near-native IO
> performance.
<nod> But, uh, are those patches going to be reviewed?
--D
^ permalink raw reply
* Re: [PATCH v4 2/5] ext4: call deactivate_super() in extents_kunit_exit()
From: Ojaswin Mujoo @ 2026-03-21 7:54 UTC (permalink / raw)
To: Ye Bin; +Cc: tytso, adilger.kernel, linux-ext4, jack
In-Reply-To: <20260319125434.333117-3-yebin@huaweicloud.com>
On Thu, Mar 19, 2026 at 08:54:31PM +0800, Ye Bin wrote:
> From: Ye Bin <yebin10@huawei.com>
>
> Call deactivate_super() is called in extents_kunit_exit() to cleanup
> the file system resource.
>
> Fixes: cb1e0c1d1fad ("ext4: kunit tests for extent splitting and conversion")
> Signed-off-by: Ye Bin <yebin10@huawei.com>
> Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
> ---
> fs/ext4/extents-test.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/fs/ext4/extents-test.c b/fs/ext4/extents-test.c
> index 82c59291e045..3d4663d99eb1 100644
> --- a/fs/ext4/extents-test.c
> +++ b/fs/ext4/extents-test.c
> @@ -146,6 +146,7 @@ static void extents_kunit_exit(struct kunit *test)
> struct ext4_sb_info *sbi = sb->s_fs_info;
>
> ext4_es_unregister_shrinker(sbi);
> + deactivate_super(sbi->s_sb);
> kfree(sbi);
> kfree(k_ctx.k_ei);
> kfree(k_ctx.k_data);
> --
> 2.34.1
Looks good now, thanks for taking care of this.
Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
Regards,
ojaswin
>
^ permalink raw reply
* [PATCH v2 0/3] fix s_uuid and f_fsid consistency for cloned filesystems
From: Anand Jain @ 2026-03-21 11:55 UTC (permalink / raw)
To: linux-ext4, linux-btrfs; +Cc: linux-xfs, hch
v2:
Derive statfs::f_fsid only when using new 'nouuid'; naming matches with xfs.
v1:
btrfs:
https://lore.kernel.org/linux-btrfs/cover.1772095546.git.asj@kernel.org/
ext4:
https://lore.kernel.org/linux-ext4/e269a49eed2de23eb9f9bd7f506f0fe47696a023.1772095546.git.asj@kernel.org/
Anand Jain (3):
btrfs: use on-disk uuid for s_uuid in temp_fsid mounts
btrfs: derive f_fsid from on-disk fsuuid and dev_t
ext4: derive f_fsid from block device to avoid collisions
fs/btrfs/disk-io.c | 3 ++-
fs/btrfs/fs.h | 1 +
fs/btrfs/super.c | 35 +++++++++++++++++++++++++++++++----
fs/ext4/ext4.h | 1 +
fs/ext4/super.c | 12 ++++++++++--
5 files changed, 45 insertions(+), 7 deletions(-)
--
2.43.0
^ permalink raw reply
* [PATCH v2 1/3] btrfs: use on-disk uuid for s_uuid in temp_fsid mounts
From: Anand Jain @ 2026-03-21 11:55 UTC (permalink / raw)
To: linux-ext4, linux-btrfs; +Cc: linux-xfs, hch
In-Reply-To: <cover.1774092915.git.asj@kernel.org>
When mounting a cloned filesystem with a temporary fsuuid (temp_fsid),
layered modules like overlayfs require a persistent identifier.
While the internal in-memory fs_devices->fsid must remain unique, to
distinguish each instance of the mounted filesystem, let s_uuid carry
the original on-disk UUID.
Signed-off-by: Anand Jain <asj@kernel.org>
---
fs/btrfs/disk-io.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index c835141ee384..90e0369bf682 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -3451,7 +3451,8 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
/* Update the values for the current filesystem. */
sb->s_blocksize = sectorsize;
sb->s_blocksize_bits = blksize_bits(sectorsize);
- memcpy(&sb->s_uuid, fs_info->fs_devices->fsid, BTRFS_FSID_SIZE);
+ /* Copy on-disk uuid, even for temp_fsid mounts */
+ memcpy(&sb->s_uuid, fs_info->super_copy->fsid, BTRFS_FSID_SIZE);
mutex_lock(&fs_info->chunk_mutex);
ret = btrfs_read_sys_array(fs_info);
--
2.43.0
^ permalink raw reply related
* [PATCH v2 2/3] btrfs: derive f_fsid from on-disk fsuuid and dev_t
From: Anand Jain @ 2026-03-21 11:55 UTC (permalink / raw)
To: linux-ext4, linux-btrfs; +Cc: linux-xfs, hch
In-Reply-To: <cover.1774092915.git.asj@kernel.org>
f_fsid depends on fs_devices->fsid and subvol root id.
For cloned devices either same as the source or dynamical generated
at mount won't suite because tools like fanotify and ima depends on it.
Switch to a stable derivation using the persistent on-disk fsuuid +
root id + devt of the block device for the single device filesystem.
This is consistent as long as the device remains unchanged/replace
(excludes btrfs device replace secnario for now).
This change is only for the single device configs and is behind the
-o nouuid mount option to keep this change compatible with ABI.
Signed-off-by: Anand Jain <asj@kernel.org>
---
fs/btrfs/fs.h | 1 +
fs/btrfs/super.c | 35 +++++++++++++++++++++++++++++++----
2 files changed, 32 insertions(+), 4 deletions(-)
diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h
index a4758d94b32e..6e2a5c2bd03c 100644
--- a/fs/btrfs/fs.h
+++ b/fs/btrfs/fs.h
@@ -270,6 +270,7 @@ enum {
BTRFS_MOUNT_IGNOREMETACSUMS = (1ULL << 31),
BTRFS_MOUNT_IGNORESUPERFLAGS = (1ULL << 32),
BTRFS_MOUNT_REF_TRACKER = (1ULL << 33),
+ BTRFS_MOUNT_NOUUID = (1ULL << 34),
};
/* These mount options require a full read-only fs, no new transaction is allowed. */
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 125fca57c164..2fb82032f1e1 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -102,6 +102,7 @@ enum {
Opt_compress_type,
Opt_degraded,
Opt_device,
+ Opt_nouuid,
Opt_fatal_errors,
Opt_flushoncommit,
Opt_max_inline,
@@ -227,6 +228,7 @@ static const struct fs_parameter_spec btrfs_fs_parameters[] = {
fsparam_flag_no("datasum", Opt_datasum),
fsparam_flag("degraded", Opt_degraded),
fsparam_string("device", Opt_device),
+ fsparam_flag("nouuid", Opt_nouuid),
fsparam_flag_no("discard", Opt_discard),
fsparam_enum("discard", Opt_discard_mode, btrfs_parameter_discard),
fsparam_enum("fatal_errors", Opt_fatal_errors, btrfs_parameter_fatal_errors),
@@ -382,6 +384,9 @@ static int btrfs_parse_param(struct fs_context *fc, struct fs_parameter *param)
return PTR_ERR(device);
break;
}
+ case Opt_nouuid:
+ btrfs_set_opt(ctx->mount_opt, NOUUID);
+ break;
case Opt_datasum:
if (result.negated) {
btrfs_set_opt(ctx->mount_opt, NODATASUM);
@@ -1113,6 +1118,8 @@ static int btrfs_show_options(struct seq_file *seq, struct dentry *dentry)
seq_puts(seq, ",discard");
if (btrfs_test_opt(info, DISCARD_ASYNC))
seq_puts(seq, ",discard=async");
+ if (btrfs_test_opt(info, NOUUID))
+ seq_puts(seq, ",nouuid");
if (!(info->sb->s_flags & SB_POSIXACL))
seq_puts(seq, ",noacl");
if (btrfs_free_space_cache_v1_active(info))
@@ -1733,7 +1740,7 @@ static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf)
u64 total_free_data = 0;
u64 total_free_meta = 0;
u32 bits = fs_info->sectorsize_bits;
- __be32 *fsid = (__be32 *)fs_info->fs_devices->fsid;
+ __be32 *fsid;
unsigned factor = 1;
struct btrfs_block_rsv *block_rsv = &fs_info->global_block_rsv;
int ret;
@@ -1819,15 +1826,35 @@ static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf)
buf->f_bsize = fs_info->sectorsize;
buf->f_namelen = BTRFS_NAME_LEN;
- /* We treat it as constant endianness (it doesn't matter _which_)
- because we want the fsid to come out the same whether mounted
- on a big-endian or little-endian host */
+ /*
+ * fs_devices->fsid is dynamically generated when temp_fsid is active
+ * to support cloned devices. Use the original on-disk fsid instead,
+ * as it remains consistent across mount cycles.
+ */
+ fsid = (__be32 *)fs_info->super_copy->fsid;
+ /*
+ * We treat it as constant endianness (it doesn't matter _which_)
+ * because we want the fsid to come out the same whether mounted
+ * on a big-endian or little-endian host.
+ */
buf->f_fsid.val[0] = be32_to_cpu(fsid[0]) ^ be32_to_cpu(fsid[2]);
buf->f_fsid.val[1] = be32_to_cpu(fsid[1]) ^ be32_to_cpu(fsid[3]);
/* Mask in the root object ID too, to disambiguate subvols */
buf->f_fsid.val[0] ^= btrfs_root_id(BTRFS_I(d_inode(dentry))->root) >> 32;
buf->f_fsid.val[1] ^= btrfs_root_id(BTRFS_I(d_inode(dentry))->root);
+ /*
+ * dev_t provides way to differentiate mounted cloned devices keeps
+ * the statfs fid is consistent and unique.
+ */
+ if (btrfs_test_opt(fs_info, NOUUID) &&
+ fs_info->fs_devices->total_devices == 1) {
+ __kernel_fsid_t dev_fsid = \
+ u64_to_fsid(huge_encode_dev(fs_info->fs_devices->latest_dev->bdev->bd_dev));
+ buf->f_fsid.val[0] ^= dev_fsid.val[1];
+ buf->f_fsid.val[1] ^= dev_fsid.val[0];
+ }
+
return 0;
}
--
2.43.0
^ permalink raw reply related
* [PATCH v2 3/3] ext4: derive f_fsid from block device to avoid collisions
From: Anand Jain @ 2026-03-21 11:55 UTC (permalink / raw)
To: linux-ext4, linux-btrfs; +Cc: linux-xfs, hch
In-Reply-To: <cover.1774092915.git.asj@kernel.org>
statfs() currently reports f_fsid derived from the on-disk UUID.
Cloned block devices share the same UUID, so distinct ext4 instances
can return identical f_fsid values. This leads to collisions in
fanotify.
Encode sb->s_dev into f_fsid instead of using the superblock UUID.
This provides a per-device identifier and avoids conflicts when
filesystem is cloned, matching the behavior with xfs.
Place this change behind the new mount option "-o nouuid" for ABI
compatibility.
Signed-off-by: Anand Jain <asj@kernel.org>
---
fs/ext4/ext4.h | 1 +
fs/ext4/super.c | 12 ++++++++++--
2 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 293f698b7042..64d98ab64c11 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1282,6 +1282,7 @@ struct ext4_inode_info {
* scanning in mballoc
*/
#define EXT4_MOUNT2_ABORT 0x00000100 /* Abort filesystem */
+#define EXT4_MOUNT2_NOUUID 0x00000200 /* No duplicate f_fsid for cloned filesystem */
#define clear_opt(sb, opt) EXT4_SB(sb)->s_mount_opt &= \
~EXT4_MOUNT_##opt
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 43f680c750ae..65b712af4ad7 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1664,7 +1664,7 @@ static const struct export_operations ext4_export_ops = {
enum {
Opt_bsd_df, Opt_minix_df, Opt_grpid, Opt_nogrpid,
Opt_resgid, Opt_resuid, Opt_sb,
- Opt_nouid32, Opt_debug, Opt_removed,
+ Opt_nouid32, Opt_debug, Opt_removed, Opt_nouuid,
Opt_user_xattr, Opt_acl,
Opt_auto_da_alloc, Opt_noauto_da_alloc, Opt_noload,
Opt_commit, Opt_min_batch_time, Opt_max_batch_time, Opt_journal_dev,
@@ -1743,6 +1743,7 @@ static const struct fs_parameter_spec ext4_param_specs[] = {
fsparam_u32 ("sb", Opt_sb),
fsparam_enum ("errors", Opt_errors, ext4_param_errors),
fsparam_flag ("nouid32", Opt_nouid32),
+ fsparam_flag ("nouuid", Opt_nouuid),
fsparam_flag ("debug", Opt_debug),
fsparam_flag ("oldalloc", Opt_removed),
fsparam_flag ("orlov", Opt_removed),
@@ -1898,6 +1899,7 @@ static const struct mount_opts {
{Opt_acl, 0, MOPT_NOSUPPORT},
#endif
{Opt_nouid32, EXT4_MOUNT_NO_UID32, MOPT_SET},
+ {Opt_nouuid, EXT4_MOUNT2_NOUUID, MOPT_SET | MOPT_2},
{Opt_debug, EXT4_MOUNT_DEBUG, MOPT_SET},
{Opt_quota, EXT4_MOUNT_QUOTA | EXT4_MOUNT_USRQUOTA, MOPT_SET | MOPT_Q},
{Opt_usrquota, EXT4_MOUNT_QUOTA | EXT4_MOUNT_USRQUOTA,
@@ -2389,6 +2391,9 @@ static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param)
return -EINVAL;
}
return 0;
+ case Opt_nouuid:
+ ctx_set_mount_opt2(ctx, EXT4_MOUNT2_NOUUID);
+ return 0;
}
/*
@@ -6941,7 +6946,10 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)
buf->f_files = le32_to_cpu(es->s_inodes_count);
buf->f_ffree = percpu_counter_sum_positive(&sbi->s_freeinodes_counter);
buf->f_namelen = EXT4_NAME_LEN;
- buf->f_fsid = uuid_to_fsid(es->s_uuid);
+ if (test_opt2(sb, NOUUID))
+ buf->f_fsid = u64_to_fsid(huge_encode_dev(sb->s_bdev->bd_dev));
+ else
+ buf->f_fsid = uuid_to_fsid(es->s_uuid);
#ifdef CONFIG_QUOTA
if (ext4_test_inode_flag(dentry->d_inode, EXT4_INODE_PROJINHERIT) &&
--
2.43.0
^ permalink raw reply related
* [PATCH v2 0/9] fstests: add test coverage for cloned filesystem ids
From: Anand Jain @ 2026-03-21 11:58 UTC (permalink / raw)
To: fstests; +Cc: linux-btrfs, linux-ext4, linux-xfs, amir73il, zlang, hch
v2:
- Add new helper _require_nouuid_mountopt() inline with the btrfs and
ext4 nouuid mount option, matching xfs.
- Clean up background processes in the _cleanup().
- Use fsnotifywait(1) instead of a local tool.
- Drop the use of SCRATCH_DEV_POOL for generic test cases to keep the
existing xfs/ext4 test environment compatible for better coverage.
v1:
https://lore.kernel.org/fstests/cover.1772095513.git.asj@kernel.org
Anand Jain (9):
fstests: add _require_nouuid_mountopt() helper
fstests: add _loop_image_create_clone() helper
fstests: add _clone_mount_option() helper
fstests: add test for inotify isolation on cloned devices
fstests: verify fanotify isolation on cloned filesystems
fstests: verify f_fsid for cloned filesystems
fstests: verify libblkid resolution of duplicate UUIDs
fstests: verify IMA isolation on cloned filesystems
fstests: verify exportfs file handles on cloned filesystems
common/config | 2 +
common/rc | 74 +++++++++++++++++++++++++++
tests/generic/790 | 89 ++++++++++++++++++++++++++++++++
tests/generic/790.out | 7 +++
tests/generic/791 | 116 ++++++++++++++++++++++++++++++++++++++++++
tests/generic/791.out | 7 +++
tests/generic/792 | 62 ++++++++++++++++++++++
tests/generic/792.out | 7 +++
tests/generic/793 | 76 +++++++++++++++++++++++++++
tests/generic/793.out | 19 +++++++
tests/generic/794 | 103 +++++++++++++++++++++++++++++++++++++
tests/generic/794.out | 10 ++++
tests/generic/795 | 73 ++++++++++++++++++++++++++
tests/generic/795.out | 2 +
14 files changed, 647 insertions(+)
create mode 100644 tests/generic/790
create mode 100644 tests/generic/790.out
create mode 100644 tests/generic/791
create mode 100644 tests/generic/791.out
create mode 100644 tests/generic/792
create mode 100644 tests/generic/792.out
create mode 100644 tests/generic/793
create mode 100644 tests/generic/793.out
create mode 100644 tests/generic/794
create mode 100644 tests/generic/794.out
create mode 100644 tests/generic/795
create mode 100644 tests/generic/795.out
--
2.43.0
^ permalink raw reply
* [PATCH v2 1/9] fstests: add _require_nouuid_mountopt() helper
From: Anand Jain @ 2026-03-21 11:58 UTC (permalink / raw)
To: fstests; +Cc: linux-btrfs, linux-ext4, linux-xfs, amir73il, zlang, hch
In-Reply-To: <cover.1774090817.git.asj@kernel.org>
Add a helper function to check if the filesystem supports the -o nouuid
mount option. The helper attempts a scratch mount with -o nouuid and
skips the test if the option is not supported by the filesystem.
Signed-off-by: Anand Jain <asj@kernel.org>
---
common/rc | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/common/rc b/common/rc
index fd4ca9641822..9e3041beee2f 100644
--- a/common/rc
+++ b/common/rc
@@ -6149,6 +6149,15 @@ _require_max_file_range_blocks()
esac
}
+_require_nouuid_mountopt()
+{
+ _require_scratch
+ _scratch_mkfs > /dev/null 2>&1 || _notrun "mkfs failed"
+ _try_scratch_mount -o nouuid > /dev/null 2>&1 || \
+ _notrun "mount -o nouuid not supported by $FSTYP"
+ _scratch_unmount
+}
+
################################################################################
# make sure this script returns success
/bin/true
--
2.43.0
^ permalink raw reply related
* [PATCH v2 2/9] fstests: add _loop_image_create_clone() helper
From: Anand Jain @ 2026-03-21 11:58 UTC (permalink / raw)
To: fstests; +Cc: linux-btrfs, linux-ext4, linux-xfs, amir73il, zlang, hch
In-Reply-To: <cover.1774090817.git.asj@kernel.org>
Introduce _loop_image_create_clone() and _loop_image_destroy() to mkfs an
image file and clone it to another image file, and attach a loop device to
them. And its destroy part.
Signed-off-by: Anand Jain <asj@kernel.org>
---
common/rc | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 51 insertions(+)
diff --git a/common/rc b/common/rc
index 9e3041beee2f..a8e6af5d1a1b 100644
--- a/common/rc
+++ b/common/rc
@@ -1503,6 +1503,57 @@ _scratch_resvblks()
esac
}
+_loop_image_create_clone()
+{
+ local -n _ret=$1
+ local img_file=$TEST_DIR/${seq}.img
+ local img_file_clone=$TEST_DIR/${seq}_clone.img
+ local size=$(_small_fs_size_mb 128) # Smallest possible
+ local loop_devs
+ local temp_mnt=$TEST_DIR/${seq}_mnt
+
+ size=$((size * 1024 * 1024))
+ $XFS_IO_PROG -f -c "truncate $size" $img_file
+
+ case $FSTYP in
+ xfs)
+ _mkfs_dev "-s size=4096" $img_file
+ ;;
+ *)
+ _mkfs_dev $img_file
+ ;;
+ esac
+
+ case $FSTYP in
+ btrfs)
+ loop_devs=$(_create_loop_device $img_file)
+ mkdir -p $temp_mnt
+ _mount $loop_devs $temp_mnt
+ $BTRFS_UTIL_PROG subvolume create $temp_mnt/sv1 &> /dev/null
+ _unmount $temp_mnt
+ rm -r -f $temp_mnt
+ _destroy_loop_device $loop_devs
+ ;;
+ *)
+ ;;
+ esac
+
+ cp $img_file $img_file_clone
+
+ loop_devs=$(_create_loop_device $img_file)
+ loop_devs="$loop_devs $(_create_loop_device $img_file_clone)"
+
+ _ret=($loop_devs)
+}
+
+_loop_image_destroy()
+{
+ for d in "$@"; do
+ local f=$(losetup --noheadings --output BACK-FILE $d)
+ _destroy_loop_device "$d"
+ [ -n "$f" ] && rm -f "$f"
+ done
+}
# Repair scratch filesystem. Returns 0 if the FS is good to go (either no
# errors found or errors were fixed) and nonzero otherwise; also spits out
--
2.43.0
^ permalink raw reply related
* [PATCH v2 3/9] fstests: add _clone_mount_option() helper
From: Anand Jain @ 2026-03-21 11:58 UTC (permalink / raw)
To: fstests; +Cc: linux-btrfs, linux-ext4, linux-xfs, amir73il, zlang, hch
In-Reply-To: <cover.1774090817.git.asj@kernel.org>
Adds _clone_mount_option() helper function to handle filesystem-specific
requirements for mounting cloned devices. Abstract the need for -o nouuid
on XFS.
Signed-off-by: Anand Jain <asj@kernel.org>
---
common/rc | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/common/rc b/common/rc
index a8e6af5d1a1b..7ff9815d88e3 100644
--- a/common/rc
+++ b/common/rc
@@ -397,6 +397,20 @@ _scratch_mount_options()
$SCRATCH_DEV $SCRATCH_MNT
}
+_clone_mount_option()
+{
+ local mount_opts=""
+
+ case "$FSTYP" in
+ ext4|xfs|btrfs)
+ mount_opts="-o nouuid"
+ ;;
+ *)
+ esac
+
+ echo $mount_opts
+}
+
_supports_filetype()
{
local dir=$1
--
2.43.0
^ permalink raw reply related
* [PATCH v2 4/9] fstests: add test for inotify isolation on cloned devices
From: Anand Jain @ 2026-03-21 11:58 UTC (permalink / raw)
To: fstests; +Cc: linux-btrfs, linux-ext4, linux-xfs, amir73il, zlang, hch
In-Reply-To: <cover.1774090817.git.asj@kernel.org>
Add a new test, to verify that the kernel correctly differentiates between
two block devices sharing the same FSID/UUID.
Signed-off-by: Anand Jain <asj@kernel.org>
---
common/config | 1 +
tests/generic/790 | 89 +++++++++++++++++++++++++++++++++++++++++++
tests/generic/790.out | 7 ++++
3 files changed, 97 insertions(+)
create mode 100644 tests/generic/790
create mode 100644 tests/generic/790.out
diff --git a/common/config b/common/config
index 1420e35ddfee..c08f828575a2 100644
--- a/common/config
+++ b/common/config
@@ -228,6 +228,7 @@ export BTRFS_MAP_LOGICAL_PROG=$(type -P btrfs-map-logical)
export PARTED_PROG="$(type -P parted)"
export XFS_PROPERTY_PROG="$(type -P xfs_property)"
export FSCRYPTCTL_PROG="$(type -P fscryptctl)"
+export INOTIFYWAIT_PROG="$(type -P inotifywait)"
# udev wait functions.
#
diff --git a/tests/generic/790 b/tests/generic/790
new file mode 100644
index 000000000000..3d9036ee095f
--- /dev/null
+++ b/tests/generic/790
@@ -0,0 +1,89 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Anand Jain <asj@kernel.org>. All Rights Reserved.
+#
+# FS QA Test 790
+#
+# Verify if the kernel or userspace becomes confused when two block devices
+# share the same fid/fsid/uuid. Create inotify on both original and cloned
+# filesystem. Monitor the notification in the respective logs.
+
+. ./common/preamble
+
+_begin_fstest auto quick mount clone
+
+_require_test
+_require_loop
+_require_nouuid_mountopt
+_require_command "$INOTIFYWAIT_PROG" inotifywait
+
+_cleanup()
+{
+ cd /
+ [[ -n $pid1 ]] && { kill -TERM "$pid1" 2> /dev/null; wait $pid1; }
+ [[ -n $pid2 ]] && { kill -TERM "$pid2" 2> /dev/null; wait $pid2; }
+ rm -r -f $tmp.*
+ _unmount $mnt1 2>/dev/null
+ _unmount $mnt2 2>/dev/null
+ _loop_image_destroy "${devs[@]}" 2> /dev/null
+}
+
+devs=()
+_loop_image_create_clone devs
+mkdir -p $TEST_DIR/$seq
+mnt1=$TEST_DIR/$seq/mnt1
+mnt2=$TEST_DIR/$seq/mnt2
+mkdir -p $mnt1
+mkdir -p $mnt2
+
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+ _fail "Failed to mount dev1"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+ _fail "Failed to mount dev2"
+
+log1=$tmp.inotify1
+log2=$tmp.inotify2
+
+pid1=""
+pid2=""
+echo "Setup inotify watchers on both mnt1 and mnt2"
+$INOTIFYWAIT_PROG -m -e create --format '%f' $mnt1 > $log1 2>&1 &
+pid1=$!
+$INOTIFYWAIT_PROG -m -e create --format '%f' $mnt2 > $log2 2>&1 &
+pid2=$!
+sleep 2
+
+echo "Trigger file creation on mnt1"
+touch $mnt1/file_on_mnt1
+sync
+sleep 1
+
+echo "Trigger file creation on mnt2"
+touch $mnt2/file_on_mnt2
+sync
+sleep 1
+
+echo "Verify inotify isolation"
+kill $pid1 $pid2
+wait $pid1 $pid2 2>/dev/null
+pid1=""
+pid2=""
+
+if grep -q "file_on_mnt1" $log1 && ! grep -q "file_on_mnt2" $log1; then
+ echo "SUCCESS: mnt1 events isolated."
+else
+ echo "FAIL: mnt1 inotify confusion!"
+ [ ! -s $log1 ] && echo " - mnt1 received no events."
+ grep -q "file_on_mnt2" $log1 && echo " - mnt1 received event from mnt2."
+fi
+
+if grep -q "file_on_mnt2" $log2 && ! grep -q "file_on_mnt1" $log2; then
+ echo "SUCCESS: mnt2 events isolated."
+else
+ echo "FAIL: mnt2 inotify confusion!"
+ [ ! -s $log2 ] && echo " - mnt2 received no events."
+ grep -q "file_on_mnt1" $log2 && echo " - mnt2 received event from mnt1."
+fi
+
+status=0
+exit
diff --git a/tests/generic/790.out b/tests/generic/790.out
new file mode 100644
index 000000000000..6fc08af59301
--- /dev/null
+++ b/tests/generic/790.out
@@ -0,0 +1,7 @@
+QA output created by 790
+Setup inotify watchers on both mnt1 and mnt2
+Trigger file creation on mnt1
+Trigger file creation on mnt2
+Verify inotify isolation
+SUCCESS: mnt1 events isolated.
+SUCCESS: mnt2 events isolated.
--
2.43.0
^ permalink raw reply related
* [PATCH v2 5/9] fstests: verify fanotify isolation on cloned filesystems
From: Anand Jain @ 2026-03-21 11:58 UTC (permalink / raw)
To: fstests; +Cc: linux-btrfs, linux-ext4, linux-xfs, amir73il, zlang, hch
In-Reply-To: <cover.1774090817.git.asj@kernel.org>
Verify that fanotify events are correctly routed to the appropriate
watcher when cloned filesystems are mounted.
Helps verify kernel's event notification distinguishes between devices
sharing the same FSID/UUID.
Signed-off-by: Anand Jain <asj@kernel.org>
---
common/config | 1 +
tests/generic/791 | 116 ++++++++++++++++++++++++++++++++++++++++++
tests/generic/791.out | 7 +++
3 files changed, 124 insertions(+)
create mode 100644 tests/generic/791
create mode 100644 tests/generic/791.out
diff --git a/common/config b/common/config
index c08f828575a2..73a2a91df9f9 100644
--- a/common/config
+++ b/common/config
@@ -229,6 +229,7 @@ export PARTED_PROG="$(type -P parted)"
export XFS_PROPERTY_PROG="$(type -P xfs_property)"
export FSCRYPTCTL_PROG="$(type -P fscryptctl)"
export INOTIFYWAIT_PROG="$(type -P inotifywait)"
+export FSNOTIFYWAIT_PROG="$(type -P fsnotifywait)"
# udev wait functions.
#
diff --git a/tests/generic/791 b/tests/generic/791
new file mode 100644
index 000000000000..26656cf8e450
--- /dev/null
+++ b/tests/generic/791
@@ -0,0 +1,116 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Anand Jain <asj@kernel.org>. All Rights Reserved.
+#
+# FS QA Test 791
+# Verify fanotify FID functionality on cloned filesystems by setting up
+# watchers and making sure notifications are in the correct logs files.
+
+. ./common/preamble
+
+_begin_fstest auto quick mount clone
+
+_require_test
+_require_loop
+_require_nouuid_mountopt
+_require_command "$FSNOTIFYWAIT_PROG" fsnotifywait
+
+[ "$FSTYP" = "ext4" ] && _fixed_by_kernel_commit xxxxxxxxxxxx \
+ "ext4: derive f_fsid from block device to avoid collisions"
+
+_cleanup()
+{
+ cd /
+ [[ -n $pid1 ]] && { kill -TERM "$pid1" 2> /dev/null; wait $pid1; }
+ [[ -n $pid2 ]] && { kill -TERM "$pid2" 2> /dev/null; wait $pid2; }
+ umount $mnt1 $mnt2 2>/dev/null
+ _loop_image_destroy "${devs[@]}" 2> /dev/null
+ rm -r -f $tmp.*
+}
+
+monitor_fanotify()
+{
+ local mmnt=$1
+ exec stdbuf -oL $FSNOTIFYWAIT_PROG -m -F -S -e create "$mmnt" 2>&1
+}
+
+fsid_to_fid_parts()
+{
+ local fsid=$1
+ # Pad to 16 hex chars (64-bit), then split into two 32-bit halves
+ local padded=$(printf '%016x' "0x${fsid}")
+ local hi=$(printf '%x' "0x${padded:0:8}") # strips leading zeros
+ local lo=$(printf '%x' "0x${padded:8:8}") # strips leading zeros
+ echo "${hi}.${lo}"
+}
+
+devs=()
+_loop_image_create_clone devs
+mkdir -p $TEST_DIR/$seq
+mnt1=$TEST_DIR/$seq/mnt1
+mnt2=$TEST_DIR/$seq/mnt2
+mkdir -p $mnt1
+mkdir -p $mnt2
+
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[0]} $mnt1 || \
+ _fail "Failed to mount dev1"
+_mount $(_common_dev_mount_options) $(_clone_mount_option) ${devs[1]} $mnt2 || \
+ _fail "Failed to mount dev2"
+
+fsid1=$(stat -f -c "%i" $mnt1)
+fsid2=$(stat -f -c "%i" $mnt2)
+
+[[ "$fsid1" == "$fsid2" ]] && \
+ _notrun "Require clone filesystem with unique f_fsid"
+
+log1=$tmp.fanotify1
+log2=$tmp.fanotify2
+
+pid1=""
+pid2=""
+echo "Setup FID fanotify watchers on both mnt1 and mnt2"
+( monitor_fanotify "$mnt1" > "$log1" ) &
+pid1=$!
+( monitor_fanotify "$mnt2" > "$log2" ) &
+pid2=$!
+sleep 2
+
+echo "Trigger file creation on mnt1"
+touch $mnt1/file_on_mnt1
+sync
+sleep 1
+
+echo "Trigger file creation on mnt2"
+touch $mnt2/file_on_mnt2
+sync
+sleep 1
+
+echo "Verify fsid in the fanotify"
+kill $pid1 $pid2
+wait $pid1 $pid2 2>/dev/null
+pid1=""
+pid2=""
+
+e_fsid1=$(fsid_to_fid_parts "$fsid1")
+e_fsid2=$(fsid_to_fid_parts "$fsid2")
+
+echo $fsid1 $e_fsid1 $fsid2 $e_fsid2 >> $seqres.full
+cat $log1 >> $seqres.full
+cat $log2 >> $seqres.full
+
+if grep -qF "$e_fsid1" "$log1" && ! grep -qF "$e_fsid2" "$log1"; then
+ echo "SUCCESS: mnt1 events found"
+else
+ [ ! -s "$log1" ] && echo " - mnt1 received no events."
+ grep -qF "$e_fsid2" "$log1" && echo " - mnt1 received event from mnt2."
+fi
+
+if grep -qF "$e_fsid2" "$log2" && ! grep -qF "$e_fsid1" "$log2"; then
+ echo "SUCCESS: mnt2 events found"
+else
+ [ ! -s "$log2" ] && echo " - mnt2 received no events."
+ grep -qF "$e_fsid1" "$log2" && echo " - mnt2 received event from mnt1."
+fi
+
+status=0
+exit
diff --git a/tests/generic/791.out b/tests/generic/791.out
new file mode 100644
index 000000000000..50e494647388
--- /dev/null
+++ b/tests/generic/791.out
@@ -0,0 +1,7 @@
+QA output created by 791
+Setup FID fanotify watchers on both mnt1 and mnt2
+Trigger file creation on mnt1
+Trigger file creation on mnt2
+Verify fsid in the fanotify
+SUCCESS: mnt1 events found
+SUCCESS: mnt2 events found
--
2.43.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox