]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
btrfs: raid56: prepare generate_pq_vertical() for bs > ps cases
authorQu Wenruo <wqu@suse.com>
Thu, 13 Nov 2025 09:40:38 +0000 (20:10 +1030)
committerDavid Sterba <dsterba@suse.com>
Tue, 25 Nov 2025 00:47:05 +0000 (01:47 +0100)
Unlike btrfs_calculate_block_csum_pages(), we cannot handle multiple
pages at the same time for P/Q generation.

So here we introduce a new @step_nr, and various helpers to grab the
sub-block page from the rbio, and generate the P/Q stripe page by page.

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

index 7f01178be7d8f183145fc2443b61673c9723040b..209f013e4da94e5052e427713124658c6fa65b4e 100644 (file)
@@ -711,20 +711,25 @@ static phys_addr_t rbio_stripe_paddr(const struct btrfs_raid_bio *rbio,
        return rbio->stripe_paddrs[rbio_paddr_index(rbio, stripe_nr, sector_nr, 0)];
 }
 
-/* Grab a paddr inside P stripe */
-static phys_addr_t rbio_pstripe_paddr(const struct btrfs_raid_bio *rbio,
-                                     unsigned int sector_nr)
+static phys_addr_t rbio_stripe_step_paddr(const struct btrfs_raid_bio *rbio,
+                                         unsigned int stripe_nr, unsigned int sector_nr,
+                                         unsigned int step_nr)
 {
-       return rbio_stripe_paddr(rbio, rbio->nr_data, sector_nr);
+       return rbio->stripe_paddrs[rbio_paddr_index(rbio, stripe_nr, sector_nr, step_nr)];
 }
 
-/* Grab a paddr inside Q stripe, return INVALID_PADDR if not RAID6 */
-static phys_addr_t rbio_qstripe_paddr(const struct btrfs_raid_bio *rbio,
-                                     unsigned int sector_nr)
+static phys_addr_t rbio_pstripe_step_paddr(const struct btrfs_raid_bio *rbio,
+                                          unsigned int sector_nr, unsigned int step_nr)
+{
+       return rbio_stripe_step_paddr(rbio, rbio->nr_data, sector_nr, step_nr);
+}
+
+static phys_addr_t rbio_qstripe_step_paddr(const struct btrfs_raid_bio *rbio,
+                                          unsigned int sector_nr, unsigned int step_nr)
 {
        if (rbio->nr_data + 1 == rbio->real_stripes)
                return INVALID_PADDR;
-       return rbio_stripe_paddr(rbio, rbio->nr_data + 1, sector_nr);
+       return rbio_stripe_step_paddr(rbio, rbio->nr_data + 1, sector_nr, step_nr);
 }
 
 /*
@@ -998,6 +1003,38 @@ static phys_addr_t sector_paddr_in_rbio(struct btrfs_raid_bio *rbio,
        return rbio->stripe_paddrs[index];
 }
 
+/*
+ * Similar to sector_paddr_in_rbio(), but with extra consideration for
+ * bs > ps cases, where we can have multiple steps for a fs block.
+ */
+static phys_addr_t step_paddr_in_rbio(struct btrfs_raid_bio *rbio,
+                                     int stripe_nr, int sector_nr, int step_nr,
+                                     bool bio_list_only)
+{
+       phys_addr_t ret = INVALID_PADDR;
+       int index;
+
+       ASSERT_RBIO_STRIPE(stripe_nr >= 0 && stripe_nr < rbio->real_stripes,
+                          rbio, stripe_nr);
+       ASSERT_RBIO_SECTOR(sector_nr >= 0 && sector_nr < rbio->stripe_nsectors,
+                          rbio, sector_nr);
+       ASSERT_RBIO_SECTOR(step_nr >= 0 && step_nr < rbio->sector_nsteps,
+                          rbio, sector_nr);
+
+       index = (stripe_nr * rbio->stripe_nsectors + sector_nr) * rbio->sector_nsteps + step_nr;
+       ASSERT(index >= 0 && index < rbio->nr_sectors * rbio->sector_nsteps);
+
+       scoped_guard(spinlock, &rbio->bio_list_lock) {
+               if (rbio->bio_paddrs[index] != INVALID_PADDR || bio_list_only) {
+                       /* Don't return sector without a valid page pointer */
+                       if (rbio->bio_paddrs[index] != INVALID_PADDR)
+                               ret = rbio->bio_paddrs[index];
+                       return ret;
+               }
+       }
+       return rbio->stripe_paddrs[index];
+}
+
 /*
  * allocation and initial setup for the btrfs_raid_bio.  Not
  * this does not allocate any pages for rbio->pages.
@@ -1319,45 +1356,56 @@ static inline void *kmap_local_paddr(phys_addr_t paddr)
        return kmap_local_page(phys_to_page(paddr)) + offset_in_page(paddr);
 }
 
-/* Generate PQ for one vertical stripe. */
-static void generate_pq_vertical(struct btrfs_raid_bio *rbio, int sectornr)
+static void generate_pq_vertical_step(struct btrfs_raid_bio *rbio, unsigned int sector_nr,
+                                     unsigned int step_nr)
 {
        void **pointers = rbio->finish_pointers;
-       const u32 sectorsize = rbio->bioc->fs_info->sectorsize;
+       const u32 step = min(rbio->bioc->fs_info->sectorsize, PAGE_SIZE);
        int stripe;
        const bool has_qstripe = rbio->bioc->map_type & BTRFS_BLOCK_GROUP_RAID6;
 
        /* First collect one sector from each data stripe */
        for (stripe = 0; stripe < rbio->nr_data; stripe++)
                pointers[stripe] = kmap_local_paddr(
-                               sector_paddr_in_rbio(rbio, stripe, sectornr, 0));
+                               step_paddr_in_rbio(rbio, stripe, sector_nr, step_nr, 0));
 
        /* Then add the parity stripe */
-       set_bit(rbio_sector_index(rbio, rbio->nr_data, sectornr),
-               rbio->stripe_uptodate_bitmap);
-       pointers[stripe++] = kmap_local_paddr(rbio_pstripe_paddr(rbio, sectornr));
+       pointers[stripe++] = kmap_local_paddr(rbio_pstripe_step_paddr(rbio, sector_nr, step_nr));
 
        if (has_qstripe) {
                /*
                 * RAID6, add the qstripe and call the library function
                 * to fill in our p/q
                 */
-               set_bit(rbio_sector_index(rbio, rbio->nr_data + 1, sectornr),
-                       rbio->stripe_uptodate_bitmap);
-               pointers[stripe++] = kmap_local_paddr(rbio_qstripe_paddr(rbio, sectornr));
+               pointers[stripe++] = kmap_local_paddr(
+                               rbio_qstripe_step_paddr(rbio, sector_nr, step_nr));
 
                assert_rbio(rbio);
-               raid6_call.gen_syndrome(rbio->real_stripes, sectorsize,
-                                       pointers);
+               raid6_call.gen_syndrome(rbio->real_stripes, step, pointers);
        } else {
                /* raid5 */
-               memcpy(pointers[rbio->nr_data], pointers[0], sectorsize);
-               run_xor(pointers + 1, rbio->nr_data - 1, sectorsize);
+               memcpy(pointers[rbio->nr_data], pointers[0], step);
+               run_xor(pointers + 1, rbio->nr_data - 1, step);
        }
        for (stripe = stripe - 1; stripe >= 0; stripe--)
                kunmap_local(pointers[stripe]);
 }
 
+/* Generate PQ for one vertical stripe. */
+static void generate_pq_vertical(struct btrfs_raid_bio *rbio, int sectornr)
+{
+       const bool has_qstripe = (rbio->bioc->map_type & BTRFS_BLOCK_GROUP_RAID6);
+
+       for (int i = 0; i < rbio->sector_nsteps; i++)
+               generate_pq_vertical_step(rbio, sectornr, i);
+
+       set_bit(rbio_sector_index(rbio, rbio->nr_data, sectornr),
+               rbio->stripe_uptodate_bitmap);
+       if (has_qstripe)
+               set_bit(rbio_sector_index(rbio, rbio->nr_data + 1, sectornr),
+                       rbio->stripe_uptodate_bitmap);
+}
+
 static int rmw_assemble_write_bios(struct btrfs_raid_bio *rbio,
                                   struct bio_list *bio_list)
 {