]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
md/md-llbitmap: optimize initial sync with write_zeroes_unmap support
authorYu Kuai <yukuai@fnnas.com>
Mon, 23 Mar 2026 05:46:44 +0000 (13:46 +0800)
committerYu Kuai <yukuai@fnnas.com>
Tue, 7 Apr 2026 05:09:22 +0000 (13:09 +0800)
For RAID-456 arrays with llbitmap, if all underlying disks support
write_zeroes with unmap, issue write_zeroes to zero all disk data
regions and initialize the bitmap to BitCleanUnwritten instead of
BitUnwritten.

This optimization skips the initial XOR parity building because:
1. write_zeroes with unmap guarantees zeroed reads after the operation
2. For RAID-456, when all data is zero, parity is automatically
   consistent (0 XOR 0 XOR ... = 0)
3. BitCleanUnwritten indicates parity is valid but no user data
   has been written

The implementation adds two helper functions:
- llbitmap_all_disks_support_wzeroes_unmap(): Checks if all active
  disks support write_zeroes with unmap
- llbitmap_zero_all_disks(): Issues blkdev_issue_zeroout() to each
  rdev's data region to zero all disks

The zeroing and bitmap state setting happens in llbitmap_init_state()
during bitmap initialization. If any disk fails to zero, we fall back
to BitUnwritten and normal lazy recovery.

This significantly reduces array initialization time for RAID-456
arrays built on modern NVMe SSDs or other devices that support
write_zeroes with unmap.

Reviewed-by: Xiao Ni <xni@redhat.com>
Link: https://lore.kernel.org/linux-raid/20260323054644.3351791-4-yukuai@fnnas.com/
Signed-off-by: Yu Kuai <yukuai@fnnas.com>
drivers/md/md-llbitmap.c

index f10374242c9a59a3001351b74f27c9d0a5e7d92e..9e7e6b1a6f157582fd173a4d33c268ea9e11e3f6 100644 (file)
@@ -654,13 +654,73 @@ static int llbitmap_cache_pages(struct llbitmap *llbitmap)
        return 0;
 }
 
+/*
+ * Check if all underlying disks support write_zeroes with unmap.
+ */
+static bool llbitmap_all_disks_support_wzeroes_unmap(struct llbitmap *llbitmap)
+{
+       struct mddev *mddev = llbitmap->mddev;
+       struct md_rdev *rdev;
+
+       rdev_for_each(rdev, mddev) {
+               if (rdev->raid_disk < 0 || test_bit(Faulty, &rdev->flags))
+                       continue;
+
+               if (bdev_write_zeroes_unmap_sectors(rdev->bdev) == 0)
+                       return false;
+       }
+
+       return true;
+}
+
+/*
+ * Issue write_zeroes to all underlying disks to zero their data regions.
+ * This ensures parity consistency for RAID-456 (0 XOR 0 = 0).
+ * Returns true if all disks were successfully zeroed.
+ */
+static bool llbitmap_zero_all_disks(struct llbitmap *llbitmap)
+{
+       struct mddev *mddev = llbitmap->mddev;
+       struct md_rdev *rdev;
+       sector_t dev_sectors = mddev->dev_sectors;
+       int ret;
+
+       rdev_for_each(rdev, mddev) {
+               if (rdev->raid_disk < 0 || test_bit(Faulty, &rdev->flags))
+                       continue;
+
+               ret = blkdev_issue_zeroout(rdev->bdev,
+                                          rdev->data_offset,
+                                          dev_sectors,
+                                          GFP_KERNEL, 0);
+               if (ret) {
+                       pr_warn("md/llbitmap: failed to zero disk %pg: %d\n",
+                               rdev->bdev, ret);
+                       return false;
+               }
+       }
+
+       return true;
+}
+
 static void llbitmap_init_state(struct llbitmap *llbitmap)
 {
+       struct mddev *mddev = llbitmap->mddev;
        enum llbitmap_state state = BitUnwritten;
        unsigned long i;
 
-       if (test_and_clear_bit(BITMAP_CLEAN, &llbitmap->flags))
+       if (test_and_clear_bit(BITMAP_CLEAN, &llbitmap->flags)) {
                state = BitClean;
+       } else if (raid_is_456(mddev) &&
+                  llbitmap_all_disks_support_wzeroes_unmap(llbitmap)) {
+               /*
+                * All disks support write_zeroes with unmap. Zero all disks
+                * to ensure parity consistency, then set BitCleanUnwritten
+                * to skip initial sync.
+                */
+               if (llbitmap_zero_all_disks(llbitmap))
+                       state = BitCleanUnwritten;
+       }
 
        for (i = 0; i < llbitmap->chunks; i++)
                llbitmap_write(llbitmap, state, i);