diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-19 16:57:24 -1000 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-19 16:57:24 -1000 |
| commit | b9a3eae9453b7547e4ab27df53a2ee8c7eb90c01 (patch) | |
| tree | 5a1cf9b20c4d1264dcf784683f208e69e5a813e9 /include/linux | |
| parent | 355da270c1700db892b8d1c9e0eb76a801e4549a (diff) | |
| parent | 7c5bb35718a6ecc2001e2f8f6abc068ab926b583 (diff) | |
Merge tag 'vfs-7.4-rc1.bh' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
Pull buffer_head updates from Christian Brauner:
"This stops the buffer_head write completion handlers from clearing
BH_Uptodate when a write fails.
A buffer whose write failed still holds exactly the data the
filesystem asked to be written. It's the disk that is stale, not the
buffer. Claiming otherwise has consequences. mark_buffer_dirty() warns
when a filesystem redirties the buffer to retry the write, a buffer
that isn't up to date gets re-read from disk which silently replaces
the data with the stale on-disk copy, and the window between the write
completing and BH_Uptodate being cleared is visible to anyone holding
the folio lock.
BH_Write_EIO already records that the last write failed. Every
consumer is moved over to buffer_write_io_error(): the two core
helpers in fs/buffer.c, adfs, ext2, omfs, exfat, fat, ext4, ocfs2,
gfs2 and jbd2. Then write completion stops touching BH_Uptodate in
both directions and BH_Write_EIO is cleared on successful completion
instead of at submission, so a resubmission no longer hides an error
that hasn't been fixed yet. bforget() and invalidation drop the flag
so a reused block doesn't inherit someone else's write error.
The groundwork for this removes b_page from struct buffer_head and
lets a buffer_head point at memory outside the page cache. jbd2's
shadow buffers sit on a slab folio today. A slab folio overloads
->mapping so mark_buffer_write_io_error() couldn't be called on them
at all. They now point at the frozen data directly with b_folio NULL.
Also included are documentation updates from Matthew for sb_bread(),
sb_breadahead() and mmb_mark_buffer_dirty() and the removal of stale
references to unmap_buffer() and mark_buffer_dirty_inode()"
* tag 'vfs-7.4-rc1.bh' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: (27 commits)
buffer: Rename label
buffer: Refer to folios in comments
buffer: Document mmb_mark_buffer_dirty()
buffer: Document sb_bread(), sb_bread_unmovable() and sb_breadahead()
fs: Remove references to mark_buffer_dirty_inode()
buffer: clear BH_Write_EIO when a write succeeds, not when one starts
buffer: Remove reference to unmap_buffer()
buffer: stop touching BH_Uptodate on write completion
jbd2: check for a fast commit write error with buffer_write_io_error()
jbd2: say what jbd2_freeze_jh_data()'s assertion is actually checking
jbd2: report journal write errors with BH_Write_EIO
gfs2: check for a metadata write error with buffer_write_io_error()
ocfs2: check for a stale write error before reusing a metadata buffer
ocfs2: check for a metadata write error with buffer_write_io_error()
ext4: check for a metadata write error with buffer_write_io_error()
fat: check for a metadata write error with buffer_write_io_error()
exfat: check for a directory write error with buffer_write_io_error()
omfs: check for an inode write error with buffer_write_io_error()
ext2: check for an xattr block write error with buffer_write_io_error()
adfs: check for a directory write error with buffer_write_io_error()
...
Diffstat (limited to 'include/linux')
| -rw-r--r-- | include/linux/buffer_head.h | 86 |
1 files changed, 75 insertions, 11 deletions
diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index fd2c7115c054..e7a701ce029d 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -59,10 +59,7 @@ struct address_space; struct buffer_head { unsigned long b_state; /* buffer state bitmap (see above) */ struct buffer_head *b_this_page;/* circular list of page's buffers */ - union { - struct page *b_page; /* the page this bh is mapped to */ - struct folio *b_folio; /* the folio this bh is mapped to */ - }; + struct folio *b_folio; /* the folio this bh is mapped to */ sector_t b_blocknr; /* start block number */ size_t b_size; /* size of mapping */ @@ -172,7 +169,36 @@ static __always_inline int buffer_uptodate(const struct buffer_head *bh) static inline unsigned long bh_offset(const struct buffer_head *bh) { - return (unsigned long)(bh)->b_data & (page_size(bh->b_page) - 1); + return (unsigned long)(bh)->b_data & (folio_size(bh->b_folio) - 1); +} + +/** + * kmap_local_bh - Map the data of a buffer. + * @bh: The buffer. + * + * Buffers usually live in the page cache, but a few are built over memory + * which is not. Those carry no folio and b_data is already a kernel address + * which is always mapped, so there is nothing to do for them. Pair with + * kunmap_local_bh(). + * + * Return: A pointer to the buffer's data. + */ +static inline void *kmap_local_bh(const struct buffer_head *bh) +{ + if (!bh->b_folio) + return bh->b_data; + return kmap_local_folio(bh->b_folio, bh_offset(bh)); +} + +/** + * kunmap_local_bh - Unmap the data of a buffer. + * @bh: The buffer. + * @addr: The address returned by kmap_local_bh(). + */ +static inline void kunmap_local_bh(const struct buffer_head *bh, void *addr) +{ + if (bh->b_folio) + kunmap_local(addr); } /* If we *know* page->private refers to buffer_heads */ @@ -338,20 +364,58 @@ static inline void bforget(struct buffer_head *bh) __bforget(bh); } -static inline struct buffer_head * -sb_bread(struct super_block *sb, sector_t block) +/** + * sb_bread - Read a block. + * @sb: The superblock to read from. + * @block: Block number in units of block size. + * + * Read a specified block, and return the buffer head that refers + * to it. The memory is allocated from the movable area so that it can + * be migrated. The returned buffer head has its refcount increased. + * The caller should call brelse() when it has finished with the buffer. + * + * Context: May sleep waiting for I/O. + * Return: NULL if the block was unreadable. + */ +static inline +struct buffer_head *sb_bread(struct super_block *sb, sector_t block) { return __bread_gfp(sb->s_bdev, block, sb->s_blocksize, __GFP_MOVABLE); } -static inline struct buffer_head * -sb_bread_unmovable(struct super_block *sb, sector_t block) +/** + * sb_bread_unmovable - Read a block. + * @sb: The superblock to read from. + * @block: Block number in units of block size. + * + * Read a specified block, and return the buffer head that refers to it. + * The memory is allocated from the unmovable area so that pointers into + * it remain valid after compaction runs. The returned buffer head has + * its refcount increased. The caller should call brelse() when it has + * finished with the buffer. + * + * Context: May sleep waiting for I/O. + * Return: NULL if the block was unreadable. + */ +static inline +struct buffer_head *sb_bread_unmovable(struct super_block *sb, sector_t block) { return __bread_gfp(sb->s_bdev, block, sb->s_blocksize, 0); } -static inline void -sb_breadahead(struct super_block *sb, sector_t block) +/** + * sb_breadahead - Start readahead. + * @sb: Superblock identifying the block device. + * @block: The block to read. + * + * Read this block. The I/O will be flagged as being readahead rather + * than immediate read, but (unlike the page cache), surrounding blocks + * will not be read. + * + * Context: May sleep in order to allocate memory. + */ +static inline +void sb_breadahead(struct super_block *sb, sector_t block) { __breadahead(sb->s_bdev, block, sb->s_blocksize); } |
