]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
btrfs: subpage: dump the involved bitmap when ASSERT() failed
authorQu Wenruo <wqu@suse.com>
Mon, 12 Jan 2026 14:55:50 +0000 (09:55 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 17 Jan 2026 15:31:26 +0000 (16:31 +0100)
[ Upstream commit 61d730731b47eeee42ad11fc71e145d269acab8d ]

For btrfs_folio_assert_not_dirty() and btrfs_folio_set_lock(), we call
bitmap_test_range_all_zero() to ensure the involved range has no
dirty/lock bit already set.

However with my recent enhanced delalloc range error handling, I was
hitting the ASSERT() inside btrfs_folio_set_lock(), and it turns out
that some error handling path is not properly updating the folio flags.

So add some extra dumping for the ASSERTs to dump the involved bitmap
to help debug.

Reviewed-by: Boris Burkov <boris@bur.io>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Stable-dep-of: e9e3b22ddfa7 ("btrfs: fix beyond-EOF write handling")
Signed-off-by: Sasha Levin <sashal@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/btrfs/subpage.c

index 71a56aaac7ad27c320f8b44d0d3e3f0d5db764f7..a1a358addc58164f199a3b979ad53c0e8faf1526 100644 (file)
@@ -652,6 +652,28 @@ IMPLEMENT_BTRFS_PAGE_OPS(ordered, folio_set_ordered, folio_clear_ordered,
 IMPLEMENT_BTRFS_PAGE_OPS(checked, folio_set_checked, folio_clear_checked,
                         folio_test_checked);
 
+#define GET_SUBPAGE_BITMAP(subpage, fs_info, name, dst)                        \
+{                                                                      \
+       const int sectors_per_page = fs_info->sectors_per_page;         \
+                                                                       \
+       ASSERT(sectors_per_page < BITS_PER_LONG);                       \
+       *dst = bitmap_read(subpage->bitmaps,                            \
+                          sectors_per_page * btrfs_bitmap_nr_##name,   \
+                          sectors_per_page);                           \
+}
+
+#define SUBPAGE_DUMP_BITMAP(fs_info, folio, name, start, len)          \
+{                                                                      \
+       const struct btrfs_subpage *subpage = folio_get_private(folio); \
+       unsigned long bitmap;                                           \
+                                                                       \
+       GET_SUBPAGE_BITMAP(subpage, fs_info, name, &bitmap);            \
+       btrfs_warn(fs_info,                                             \
+       "dumpping bitmap start=%llu len=%u folio=%llu " #name "_bitmap=%*pbl", \
+                  start, len, folio_pos(folio),                        \
+                  fs_info->sectors_per_page, &bitmap);                 \
+}
+
 /*
  * Make sure not only the page dirty bit is cleared, but also subpage dirty bit
  * is cleared.
@@ -677,6 +699,10 @@ void btrfs_folio_assert_not_dirty(const struct btrfs_fs_info *fs_info,
        subpage = folio_get_private(folio);
        ASSERT(subpage);
        spin_lock_irqsave(&subpage->lock, flags);
+       if (unlikely(!bitmap_test_range_all_zero(subpage->bitmaps, start_bit, nbits))) {
+               SUBPAGE_DUMP_BITMAP(fs_info, folio, dirty, start, len);
+               ASSERT(bitmap_test_range_all_zero(subpage->bitmaps, start_bit, nbits));
+       }
        ASSERT(bitmap_test_range_all_zero(subpage->bitmaps, start_bit, nbits));
        spin_unlock_irqrestore(&subpage->lock, flags);
 }
@@ -706,23 +732,16 @@ void btrfs_folio_set_lock(const struct btrfs_fs_info *fs_info,
        nbits = len >> fs_info->sectorsize_bits;
        spin_lock_irqsave(&subpage->lock, flags);
        /* Target range should not yet be locked. */
-       ASSERT(bitmap_test_range_all_zero(subpage->bitmaps, start_bit, nbits));
+       if (unlikely(!bitmap_test_range_all_zero(subpage->bitmaps, start_bit, nbits))) {
+               SUBPAGE_DUMP_BITMAP(fs_info, folio, locked, start, len);
+               ASSERT(bitmap_test_range_all_zero(subpage->bitmaps, start_bit, nbits));
+       }
        bitmap_set(subpage->bitmaps, start_bit, nbits);
        ret = atomic_add_return(nbits, &subpage->nr_locked);
        ASSERT(ret <= fs_info->sectors_per_page);
        spin_unlock_irqrestore(&subpage->lock, flags);
 }
 
-#define GET_SUBPAGE_BITMAP(subpage, fs_info, name, dst)                        \
-{                                                                      \
-       const int sectors_per_page = fs_info->sectors_per_page;         \
-                                                                       \
-       ASSERT(sectors_per_page < BITS_PER_LONG);                       \
-       *dst = bitmap_read(subpage->bitmaps,                            \
-                          sectors_per_page * btrfs_bitmap_nr_##name,   \
-                          sectors_per_page);                           \
-}
-
 void __cold btrfs_subpage_dump_bitmap(const struct btrfs_fs_info *fs_info,
                                      struct folio *folio, u64 start, u32 len)
 {