summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Brauner <brauner@kernel.org>2026-09-04 12:54:52 +0200
committerChristian Brauner <brauner@kernel.org>2026-09-10 09:27:29 +0200
commit7c5bb35718a6ecc2001e2f8f6abc068ab926b583 (patch)
tree73b38691bf46dcd45f3746630ba71535124811ea
parent00db7c70bf7c6a79a548f484c76af60563a7736e (diff)
parenta21153de5aa936900aa98fd81643895c7fd2a267 (diff)
downloadlinux-next-7c5bb35718a6ecc2001e2f8f6abc068ab926b583.tar.gz
linux-next-7c5bb35718a6ecc2001e2f8f6abc068ab926b583.zip
Merge patch series "Buffer head documentation updates"
Matthew Wilcox (Oracle) <willy@infradead.org> says: Mostly just tidying up some out of date documentation, but I found the time to document some functions I thought were worth documenting. * patches from https://patch.msgid.link/20260828205429.3678204-1-willy@infradead.org: 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: Remove reference to unmap_buffer() Link: https://patch.msgid.link/20260828205429.3678204-1-willy@infradead.org Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
-rw-r--r--Documentation/filesystems/porting.rst2
-rw-r--r--fs/buffer.c91
-rw-r--r--include/linux/buffer_head.h50
3 files changed, 93 insertions, 50 deletions
diff --git a/Documentation/filesystems/porting.rst b/Documentation/filesystems/porting.rst
index 60880eb0c49d..de96b6745600 100644
--- a/Documentation/filesystems/porting.rst
+++ b/Documentation/filesystems/porting.rst
@@ -348,7 +348,7 @@ simply of return 1. Note that all actual eviction work is done by caller after
As before, clear_inode() must be called exactly once on each call of
->evict_inode() (as it used to be for each call of ->delete_inode()). Unlike
before, if you are using inode-associated metadata buffers (i.e.
-mark_buffer_dirty_inode()), it's your responsibility to call
+mmb_mark_buffer_dirty()), it's your responsibility to call
invalidate_inode_buffers() before clear_inode().
NOTE: checking i_nlink in the beginning of ->write_inode() and bailing out
diff --git a/fs/buffer.c b/fs/buffer.c
index a43f3c9ecaa0..1dc933ba6925 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -264,7 +264,7 @@ __find_get_block_slow(struct block_device *bdev, sector_t block, bool atomic)
bh = bh->b_this_page;
} while (bh != head);
- /* we might be here because some of the buffers on this page are
+ /* we might be here because some of the buffers on this folio are
* not mapped. This is due to various races between
* file io on the block device and getblk. It gets dealt with
* elsewhere, don't buffer_error if we had some unmapped buffers
@@ -310,7 +310,7 @@ static void end_buffer_async_read(struct buffer_head *bh, int uptodate)
/*
* Be _very_ careful from here on. Bad things can happen if
* two buffer heads end IO at almost the same time and both
- * decide that the page is now completely done.
+ * decide that the folio is now completely done.
*/
first = folio_buffers(folio);
spin_lock_irqsave(&first->b_uptodate_lock, flags);
@@ -518,8 +518,8 @@ EXPORT_SYMBOL_GPL(mmb_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_mmb is not NULL so we are sure the
+ * up, waiting for those writes to complete. mmb_mark_buffer_dirty()
+ * doesn't touch b_assoc_buffers list if b_mmb is set so we are sure the
* buffer stays on our list until IO completes (at which point it can be
* reaped).
*/
@@ -540,7 +540,7 @@ int mmb_sync(struct mapping_metadata_bhs *mmb)
bh = BH_ENTRY(mmb->list.next);
WARN_ON_ONCE(bh->b_mmb != mmb);
__remove_assoc_queue(mmb, bh);
- /* Avoid race with mark_buffer_dirty_inode() which does
+ /* Avoid race with mmb_mark_buffer_dirty() which does
* a lockless check and we rely on seeing the dirty bit */
smp_mb();
if (buffer_dirty(bh) || buffer_locked(bh)) {
@@ -578,7 +578,7 @@ int mmb_sync(struct mapping_metadata_bhs *mmb)
bh = BH_ENTRY(tmp.prev);
get_bh(bh);
__remove_assoc_queue(mmb, bh);
- /* Avoid race with mark_buffer_dirty_inode() which does
+ /* Avoid race with mmb_mark_buffer_dirty() which does
* a lockless check and we rely on seeing the dirty bit */
smp_mb();
if (buffer_dirty(bh)) {
@@ -616,6 +616,14 @@ void write_boundary_block(struct block_device *bdev,
}
}
+/**
+ * mmb_mark_buffer_dirty - Mark a metadata buffer dirty.
+ * @bh: The buffer to mark dirty.
+ * @mmb: The list of buffers to add the buffer to.
+ *
+ * Mark the buffer dirty and add it to the list if it is not already on
+ * a list.
+ */
void mmb_mark_buffer_dirty(struct buffer_head *bh,
struct mapping_metadata_bhs *mmb)
{
@@ -684,7 +692,7 @@ bool block_dirty_folio(struct address_space *mapping, struct folio *folio)
} while (bh != head);
}
/*
- * Lock out page's memcg migration to keep PageDirty
+ * Lock out folio's memcg migration to keep folio dirty flag
* synchronized with per-memcg dirty page counters.
*/
newly_dirty = !folio_test_set_dirty(folio);
@@ -950,23 +958,23 @@ __getblk_slow(struct block_device *bdev, sector_t block,
}
/*
- * The relationship between dirty buffers and dirty pages:
+ * The relationship between dirty buffers and dirty folios:
*
- * Whenever a page has any dirty buffers, the page's dirty bit is set, and
- * the page is tagged dirty in the page cache.
+ * Whenever a folio has any dirty buffers, the folio's dirty flag is set, and
+ * the folio is tagged dirty in the page cache.
*
* At all times, the dirtiness of the buffers represents the dirtiness of
- * subsections of the page. If the page has buffers, the page dirty bit is
+ * subsections of the folio. If the folio has buffers, the folio dirty flag is
* merely a hint about the true dirty state.
*
- * When a page is set dirty in its entirety, all its buffers are marked dirty
- * (if the page has buffers).
+ * When a folio is set dirty in its entirety, all its buffers are marked dirty
+ * (if the folio has buffers).
*
- * When a buffer is marked dirty, its page is dirtied, but the page's other
+ * When a buffer is marked dirty, its folio is dirtied, but the folio's other
* buffers are not.
*
* Also. When blockdev buffers are explicitly read with bread(), they
- * individually become uptodate. But their backing page remains not
+ * individually become uptodate. But their backing folio remains not
* uptodate - even if all of its buffers are uptodate. A subsequent
* block_read_full_folio() against that folio will discover all the uptodate
* buffers, will set the folio uptodate and will perform no I/O.
@@ -977,7 +985,7 @@ __getblk_slow(struct block_device *bdev, sector_t block,
* @bh: the buffer_head to mark dirty
*
* mark_buffer_dirty() will set the dirty bit against the buffer, then set
- * its backing page dirty, then tag the page as dirty in the page cache
+ * its backing folio dirty, then tag the folio as dirty in the page cache
* and then attach the address_space's inode to its superblock's dirty
* inode list.
*
@@ -1220,7 +1228,7 @@ static void bh_lru_install(struct buffer_head *bh)
/*
* the refcount of buffer_head in bh_lru prevents dropping the
- * attached page(i.e., try_to_free_buffers) so it could cause
+ * attached folio (i.e., try_to_free_buffers) so it could cause
* failing page migration.
* Skip putting upcoming bh into bh_lru until migration is done.
*/
@@ -1284,7 +1292,7 @@ lookup_bh_lru(struct block_device *bdev, sector_t block, unsigned size)
* Perform a pagecache lookup for the matching buffer. If it's there, refresh
* it in the LRU and mark it as accessed. If it is not present then return
* NULL. Atomic context callers may also return NULL if the buffer is being
- * migrated; similarly the page is not marked accessed either.
+ * migrated; similarly the folio is not marked accessed either.
*/
static struct buffer_head *
find_get_block_common(struct block_device *bdev, sector_t block,
@@ -1293,7 +1301,7 @@ find_get_block_common(struct block_device *bdev, sector_t block,
struct buffer_head *bh = lookup_bh_lru(bdev, block, size);
if (bh == NULL) {
- /* __find_get_block_slow will mark the page accessed */
+ /* __find_get_block_slow will mark the folio accessed */
bh = __find_get_block_slow(bdev, block, atomic);
if (bh)
bh_lru_install(bh);
@@ -1479,15 +1487,14 @@ void folio_set_bh(struct buffer_head *bh, struct folio *folio,
}
EXPORT_SYMBOL(folio_set_bh);
-/*
- * Called when truncating a buffer on a page completely.
- */
-
/* Bits that are cleared during an invalidate */
#define BUFFER_FLAGS_DISCARD \
(1 << BH_Mapped | 1 << BH_New | 1 << BH_Req | \
1 << BH_Delay | 1 << BH_Unwritten | 1 << BH_Write_EIO)
+/*
+ * Called when truncating a buffer on a folio completely.
+ */
static void discard_buffer(struct buffer_head * bh)
{
unsigned long b_state;
@@ -1615,9 +1622,7 @@ EXPORT_SYMBOL(create_empty_buffers);
* moment when something will explicitly mark the buffer dirty (hopefully that
* will not happen until we will free that block ;-) We don't even need to mark
* it not-uptodate - nobody can expect anything from a newly allocated buffer
- * anyway. We used to use unmap_buffer() for such invalidation, but that was
- * wrong. We definitely don't want to mark the alias unmapped, for example - it
- * would confuse anyone who might pick it with bread() afterwards...
+ * anyway.
*
* Also.. Note that bforget() doesn't lock the buffer. So there can be
* writeout I/O going on against recently-freed buffers. We don't wait on that
@@ -1653,7 +1658,7 @@ void clean_bdev_aliases(struct block_device *bdev, sector_t block, sector_t len)
/* Recheck when the folio is locked which pins bhs */
head = folio_buffers(folio);
if (!head)
- goto unlock_page;
+ goto unlock_folio;
bh = head;
do {
if (!buffer_mapped(bh) || (bh->b_blocknr < block))
@@ -1666,7 +1671,7 @@ void clean_bdev_aliases(struct block_device *bdev, sector_t block, sector_t len)
next:
bh = bh->b_this_page;
} while (bh != head);
-unlock_page:
+unlock_folio:
folio_unlock(folio);
}
folio_batch_release(&fbatch);
@@ -1714,7 +1719,7 @@ static struct buffer_head *folio_create_buffers(struct folio *folio,
*
* If block_write_full_folio() is called for regular writeback
* (wbc->sync_mode == WB_SYNC_NONE) then it will redirty a folio which
- * has a locked buffer. This only can happen if someone has written
+ * has a locked buffer. This can only happen if someone has written
* the buffer directly, with bh_submit(). At the address_space level
* the folio writeback flag prevents this contention from occurring.
*
@@ -2217,9 +2222,9 @@ int generic_write_end(const struct kiocb *iocb, struct address_space *mapping,
if (old_size < pos)
pagecache_isize_extended(inode, old_size, pos);
/*
- * Don't mark the inode dirty under page lock. First, it unnecessarily
- * makes the holding time of page lock longer. Second, it forces lock
- * ordering of page lock and transaction start for journaling
+ * Don't mark the inode dirty under folio lock. First, it unnecessarily
+ * makes the holding time of folio lock longer. Second, it forces lock
+ * ordering of folio lock and transaction start for journaling
* filesystems.
*/
if (i_size_changed)
@@ -2345,7 +2350,7 @@ int block_read_full_folio(struct folio *folio, get_block_t *get_block)
* BH_Async_Read tells end_buffer_async_read() that this
* buffer is not under async I/O.
*
- * The folio comes unlocked when it has no locked
+ * The folio is unlocked when it has no locked
* buffer_async buffers left.
*
* The folio lock prevents anyone starting new async
@@ -2455,7 +2460,7 @@ static int cont_expand_zero(const struct kiocb *iocb,
}
}
- /* page covers the boundary, find the boundary offset */
+ /* folio crosses the boundary, find the boundary offset */
if (index == curidx) {
zerofrom = curpos & ~PAGE_MASK;
/* if we will expand the thing last block will be filled */
@@ -2513,18 +2518,18 @@ EXPORT_SYMBOL(cont_write_begin);
/*
* block_page_mkwrite() is not allowed to change the file size as it gets
- * called from a page fault handler when a page is first dirtied. Hence we must
- * be careful to check for EOF conditions here. We set the page up correctly
- * for a written page which means we get ENOSPC checking when writing into
+ * called from a page fault handler when a folio is first dirtied. Hence we must
+ * be careful to check for EOF conditions here. We set the folio up correctly
+ * for a written folio which means we get ENOSPC checking when writing into
* holes and correct delalloc and unwritten extent mapping on filesystems that
* support these features.
*
* We are not allowed to take the i_rwsem here so we have to play games to
- * protect against truncate races as the page could now be beyond EOF. Because
- * truncate writes the inode size before removing pages, once we have the
- * page lock we can determine safely if the page is beyond EOF. If it is not
- * beyond EOF, then the page is guaranteed safe against truncation until we
- * unlock the page.
+ * protect against truncate races as the folio could now be beyond EOF. Because
+ * truncate writes the inode size before removing folios, once we have the
+ * folio lock we can determine safely if the folio is beyond EOF. If it is not
+ * beyond EOF, then the folio is guaranteed safe against truncation until we
+ * unlock the folio.
*
* Direct callers of this function should protect against filesystem freezing
* using sb_start_pagefault() - sb_end_pagefault() functions.
@@ -2542,7 +2547,7 @@ int block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf,
size = i_size_read(inode);
if ((folio->mapping != inode->i_mapping) ||
(folio_pos(folio) >= size)) {
- /* We overload EFAULT to mean page got truncated */
+ /* We overload EFAULT to mean folio got truncated */
ret = -EFAULT;
goto out_unlock;
}
diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h
index 20b8fca1abfa..e7a701ce029d 100644
--- a/include/linux/buffer_head.h
+++ b/include/linux/buffer_head.h
@@ -364,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);
}