]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
btrfs: prepare subpage operations to support more than BITS_PER_LONG sub-bitmaps
authorQu Wenruo <wqu@suse.com>
Wed, 13 May 2026 04:36:19 +0000 (14:06 +0930)
committerFilipe Manana <fdmanana@suse.com>
Tue, 9 Jun 2026 10:49:26 +0000 (11:49 +0100)
[CURRENT LIMIT]
Btrfs currently only supports sub-bitmaps (e.g. dirty bitmap) no larger
than BITS_PER_LONG.

That limit allows us to easily grab an unsigned long without the need to
properly allocate memory for a larger bitmap.

Unfortunately that limit prevents us from supporting huge folios.
For 4K page size and block size, a huge folio (order 9) means 512 blocks
inside a 2M folio.

[ENHANCEMENT]
To allow direct bitmap operations without allocating new memory,
introduce two different ways to access the subpage bitmaps:

- Return an unsigned long value
  This only happens if blocks_per_folio <= BITS_PER_LONG.

  We read out the sub-bitmap into an unsigned long, and return the
  value.
  This is the old existing method.

  This involves get_bitmap_value_##name() helper functions.
  And this time the helper functions are defined as inline functions
  instead of macros to provide better type checks.

- Return a pointer where the sub-bitmap starts
  This only happens if blocks_per_folio >= BITS_PER_LONG.

  This is the new method for sub-bitmaps larger than BITS_PER_LONG.
  Since the sizes of sub-bitmaps are all aligned to BITS_PER_LONG, we
  can directly access the start word of the sub-bitmap.

  This involves get_bitmap_pointer_##name() helper functions.

Then change the existing sub-bitmaps users to use the new helpers:

- Bitmap dumping
  Switch between get_bitmap_value_##name() and
  get_bitmap_pointer_##name() depending on the sub-bitmap size.

- btrfs_get_subpage_dirty_bitmap()
  Rename it to btrfs_get_subpage_dirty_bitmap_value() to follow the new
  value/pointer naming.
  Since we do not support huge folios yet, there is no pointer version
  for the dirty bitmap.

  Furthermore, add the support for block size == page size cases for
  btrfs_get_subpage_dirty_bitmap_value(), so that the caller no longer
  needs to check if the folio needs subpage handling.

Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/extent_io.c
fs/btrfs/subpage.c
fs/btrfs/subpage.h

index 8cf1e4c5105f491fe5d4dc9e3874994775f29f12..1dfa3152e4bd9dbb7a83ad4cd939b8995e640937 100644 (file)
@@ -1492,12 +1492,7 @@ static noinline_for_stack int writepage_delalloc(struct btrfs_inode *inode,
        int ret = 0;
 
        /* Save the dirty bitmap as our submission bitmap will be a subset of it. */
-       if (btrfs_is_subpage(fs_info, folio)) {
-               ASSERT(blocks_per_folio > 1);
-               btrfs_get_subpage_dirty_bitmap(fs_info, folio, &bio_ctrl->submit_bitmap);
-       } else {
-               bio_ctrl->submit_bitmap = 1;
-       }
+       bio_ctrl->submit_bitmap = btrfs_get_subpage_dirty_bitmap_value(fs_info, folio);
 
        for_each_set_bitrange(start_bit, end_bit, &bio_ctrl->submit_bitmap,
                              blocks_per_folio) {
index 99ae53656dbaf5c7ac8cf84ab7863b3871dd271c..fb56eaf5232584a4c3130397406bc47797e7497e 100644 (file)
@@ -555,25 +555,54 @@ IMPLEMENT_BTRFS_PAGE_OPS(dirty, folio_mark_dirty, folio_clear_dirty_for_io,
 IMPLEMENT_BTRFS_PAGE_OPS(writeback, folio_start_writeback, folio_end_writeback,
                         folio_test_writeback);
 
-#define GET_SUBPAGE_BITMAP(fs_info, folio, name, dst)                  \
-{                                                                      \
-       const unsigned int __bpf = btrfs_blocks_per_folio(fs_info, folio); \
-       const struct btrfs_folio_state *__bfs = folio_get_private(folio); \
-                                                                       \
-       ASSERT(__bpf <= BITS_PER_LONG);                                 \
-       *dst = bitmap_read(__bfs->bitmaps,                              \
-                          __bpf * btrfs_bitmap_nr_##name, __bpf);      \
+#define DEFINE_GET_SUBPAGE_BITMAP(name)                                                \
+static inline unsigned long get_bitmap_value_##name(                           \
+                                       const struct btrfs_fs_info *fs_info,    \
+                                       struct folio *folio)                    \
+{                                                                              \
+       const unsigned int __bpf = btrfs_blocks_per_folio(fs_info, folio);      \
+       const struct btrfs_folio_state *__bfs = folio_get_private(folio);       \
+       unsigned long value;                                                    \
+                                                                               \
+       ASSERT(__bpf <= BITS_PER_LONG);                                         \
+       value = bitmap_read(__bfs->bitmaps, __bpf * btrfs_bitmap_nr_##name,     \
+                            __bpf);                                            \
+       return value;                                                           \
+}                                                                              \
+static inline const unsigned long *get_bitmap_pointer_##name(                  \
+                                       const struct btrfs_fs_info *fs_info,    \
+                                       struct folio *folio)                    \
+{                                                                              \
+       const unsigned int __bpf = btrfs_blocks_per_folio(fs_info, folio);      \
+       struct btrfs_folio_state *__bfs = folio_get_private(folio);             \
+       unsigned long *pointer;                                                 \
+                                                                               \
+       ASSERT(__bpf >= BITS_PER_LONG);                                         \
+       ASSERT(IS_ALIGNED(__bpf, BITS_PER_LONG));                               \
+       pointer = __bfs->bitmaps + (BIT_WORD(__bpf) * btrfs_bitmap_nr_##name);  \
+       return pointer;                                                         \
 }
 
-#define SUBPAGE_DUMP_BITMAP(fs_info, folio, name, start, len)          \
-{                                                                      \
-       unsigned long bitmap;                                           \
-       const unsigned int __bpf = btrfs_blocks_per_folio(fs_info, folio); \
-                                                                       \
-       GET_SUBPAGE_BITMAP(fs_info, folio, name, &bitmap);              \
-       btrfs_warn(fs_info,                                             \
-       "dumping bitmap start=%llu len=%u folio=%llu " #name "_bitmap=%*pbl", \
-                  start, len, folio_pos(folio), __bpf, &bitmap);       \
+DEFINE_GET_SUBPAGE_BITMAP(uptodate);
+DEFINE_GET_SUBPAGE_BITMAP(dirty);
+DEFINE_GET_SUBPAGE_BITMAP(writeback);
+
+#define SUBPAGE_DUMP_BITMAP(fs_info, folio, name, start, len)                  \
+{                                                                              \
+       const unsigned int __bpf = btrfs_blocks_per_folio(fs_info, folio);      \
+                                                                               \
+       if (__bpf <= BITS_PER_LONG) {                                           \
+               unsigned long bitmap = get_bitmap_value_##name(fs_info, folio); \
+                                                                               \
+               btrfs_warn(fs_info,                                             \
+       "dumping bitmap start=%llu len=%u folio=%llu " #name "_bitmap=%*pbl",   \
+                  start, len, folio_pos(folio), __bpf, &bitmap);               \
+       } else {                                                                \
+               btrfs_warn(fs_info,                                             \
+       "dumping bitmap start=%llu len=%u folio=%llu " #name "_bitmap=%*pbl",   \
+                  start, len, folio_pos(folio), __bpf,                         \
+                  get_bitmap_pointer_##name(fs_info, folio));                  \
+       }                                                                       \
 }
 
 /*
@@ -663,42 +692,63 @@ void __cold btrfs_subpage_dump_bitmap(const struct btrfs_fs_info *fs_info,
 {
        struct btrfs_folio_state *bfs;
        const unsigned int blocks_per_folio = btrfs_blocks_per_folio(fs_info, folio);
-       unsigned long uptodate_bitmap;
-       unsigned long dirty_bitmap;
-       unsigned long writeback_bitmap;
        unsigned long flags;
 
        ASSERT(folio_test_private(folio) && folio_get_private(folio));
        ASSERT(blocks_per_folio > 1);
        bfs = folio_get_private(folio);
 
-       spin_lock_irqsave(&bfs->lock, flags);
-       GET_SUBPAGE_BITMAP(fs_info, folio, uptodate, &uptodate_bitmap);
-       GET_SUBPAGE_BITMAP(fs_info, folio, dirty, &dirty_bitmap);
-       GET_SUBPAGE_BITMAP(fs_info, folio, writeback, &writeback_bitmap);
-       spin_unlock_irqrestore(&bfs->lock, flags);
-
        dump_page(folio_page(folio, 0), "btrfs folio state dump");
+
+       if (blocks_per_folio <= BITS_PER_LONG) {
+               unsigned long uptodate;
+               unsigned long dirty;
+               unsigned long writeback;
+
+               spin_lock_irqsave(&bfs->lock, flags);
+               uptodate = get_bitmap_value_uptodate(fs_info, folio);
+               dirty = get_bitmap_value_dirty(fs_info, folio);
+               writeback = get_bitmap_value_writeback(fs_info, folio);
+
+               spin_unlock_irqrestore(&bfs->lock, flags);
+
+               btrfs_warn(fs_info,
+"start=%llu len=%u page=%llu, bitmaps uptodate=%*pbl dirty=%*pbl writeback=%*pbl",
+                           start, len, folio_pos(folio),
+                           blocks_per_folio, &uptodate,
+                           blocks_per_folio, &dirty,
+                           blocks_per_folio, &writeback);
+               return;
+       }
+
+       spin_lock_irqsave(&bfs->lock, flags);
        btrfs_warn(fs_info,
 "start=%llu len=%u page=%llu, bitmaps uptodate=%*pbl dirty=%*pbl writeback=%*pbl",
                    start, len, folio_pos(folio),
-                   blocks_per_folio, &uptodate_bitmap,
-                   blocks_per_folio, &dirty_bitmap,
-                   blocks_per_folio, &writeback_bitmap);
+                   blocks_per_folio, get_bitmap_pointer_uptodate(fs_info, folio),
+                   blocks_per_folio, get_bitmap_pointer_dirty(fs_info, folio),
+                   blocks_per_folio, get_bitmap_pointer_writeback(fs_info, folio));
+       spin_unlock_irqrestore(&bfs->lock, flags);
 }
 
-void btrfs_get_subpage_dirty_bitmap(struct btrfs_fs_info *fs_info,
-                                   struct folio *folio,
-                                   unsigned long *ret_bitmap)
+unsigned long btrfs_get_subpage_dirty_bitmap_value(struct btrfs_fs_info *fs_info,
+                                                  struct folio *folio)
 {
        struct btrfs_folio_state *bfs;
+       const unsigned int blocks_per_folio = btrfs_blocks_per_folio(fs_info, folio);
        unsigned long flags;
+       unsigned long value;
+
+       if (blocks_per_folio == 1)
+               return 1;
 
        ASSERT(folio_test_private(folio) && folio_get_private(folio));
-       ASSERT(btrfs_blocks_per_folio(fs_info, folio) > 1);
+       ASSERT(blocks_per_folio > 1);
+       ASSERT(blocks_per_folio <= BITS_PER_LONG);
        bfs = folio_get_private(folio);
 
        spin_lock_irqsave(&bfs->lock, flags);
-       GET_SUBPAGE_BITMAP(fs_info, folio, dirty, ret_bitmap);
+       value = get_bitmap_value_dirty(fs_info, folio);
        spin_unlock_irqrestore(&bfs->lock, flags);
+       return value;
 }
index b30eb4abae64753a5a4bd550d2ea51f42a49fbd7..756c05c89c11dfad40ddf6efae0bec6d47ee1251 100644 (file)
@@ -184,9 +184,8 @@ bool btrfs_subpage_clear_and_test_dirty(const struct btrfs_fs_info *fs_info,
 void btrfs_folio_assert_not_dirty(const struct btrfs_fs_info *fs_info,
                                  struct folio *folio, u64 start, u32 len);
 bool btrfs_meta_folio_clear_and_test_dirty(struct folio *folio, const struct extent_buffer *eb);
-void btrfs_get_subpage_dirty_bitmap(struct btrfs_fs_info *fs_info,
-                                   struct folio *folio,
-                                   unsigned long *ret_bitmap);
+unsigned long btrfs_get_subpage_dirty_bitmap_value(struct btrfs_fs_info *fs_info,
+                                                  struct folio *folio);
 void __cold btrfs_subpage_dump_bitmap(const struct btrfs_fs_info *fs_info,
                                      struct folio *folio, u64 start, u32 len);