btrfs: raid56: introduce btrfs_raid_bio::bio_sectors
authorQu Wenruo <wqu@suse.com>
Fri, 1 Apr 2022 11:23:20 +0000 (19:23 +0800)
committerDavid Sterba <dsterba@suse.com>
Mon, 16 May 2022 15:03:14 +0000 (17:03 +0200)
This new member is going to fully replace bio_pages in the future, but
for now let's keep them co-exist, until the full switch is done.

Currently cache_rbio_pages() and index_rbio_pages() will also populate
the new array.

And cache_rbio_pages() need to record which sectors are uptodate, so we
also need to introduce sector_ptr::uptodate bit.

To avoid extra memory usage, we let the new @uptodate bit to share bits
with @pgoff.  Now pgoff only has at most 31 bits, which is already more
than enough, as even for 256K page size, we only need 18 bits.

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

index 78d032323f311eb2419a84d203495e052852940e..5ee94aaed772e52c98d1fd54faaa30479b23cd9c 100644 (file)
@@ -59,7 +59,8 @@ struct btrfs_stripe_hash_table {
  */
 struct sector_ptr {
        struct page *page;
-       unsigned int pgoff;
+       unsigned int pgoff:24;
+       unsigned int uptodate:8;
 };
 
 enum btrfs_rbio_ops {
@@ -174,6 +175,9 @@ struct btrfs_raid_bio {
         */
        struct page **stripe_pages;
 
+       /* Pointers to the sectors in the bio_list, for faster lookup */
+       struct sector_ptr *bio_sectors;
+
        /*
         * pointers to the pages in the bio_list.  Stored
         * here for faster lookup
@@ -284,6 +288,24 @@ static void cache_rbio_pages(struct btrfs_raid_bio *rbio)
                copy_highpage(rbio->stripe_pages[i], rbio->bio_pages[i]);
                SetPageUptodate(rbio->stripe_pages[i]);
        }
+
+       /*
+        * This work is duplicated with the above loop, will be removed when
+        * the switch is done.
+        */
+       for (i = 0; i < rbio->nr_sectors; i++) {
+               /* Some range not covered by bio (partial write), skip it */
+               if (!rbio->bio_sectors[i].page)
+                       continue;
+
+               ASSERT(rbio->stripe_sectors[i].page);
+               memcpy_page(rbio->stripe_sectors[i].page,
+                           rbio->stripe_sectors[i].pgoff,
+                           rbio->bio_sectors[i].page,
+                           rbio->bio_sectors[i].pgoff,
+                           rbio->bioc->fs_info->sectorsize);
+               rbio->stripe_sectors[i].uptodate = 1;
+       }
        set_bit(RBIO_CACHE_READY_BIT, &rbio->flags);
 }
 
@@ -1012,7 +1034,7 @@ static struct btrfs_raid_bio *alloc_rbio(struct btrfs_fs_info *fs_info,
 
        rbio = kzalloc(sizeof(*rbio) +
                       sizeof(*rbio->stripe_pages) * num_pages +
-                      sizeof(*rbio->bio_pages) * num_pages +
+                      sizeof(*rbio->bio_sectors) * num_sectors +
                       sizeof(*rbio->stripe_sectors) * num_sectors +
                       sizeof(*rbio->finish_pointers) * real_stripes +
                       sizeof(*rbio->dbitmap) * BITS_TO_LONGS(stripe_nsectors) +
@@ -1050,6 +1072,7 @@ static struct btrfs_raid_bio *alloc_rbio(struct btrfs_fs_info *fs_info,
        } while (0)
        CONSUME_ALLOC(rbio->stripe_pages, num_pages);
        CONSUME_ALLOC(rbio->bio_pages, num_pages);
+       CONSUME_ALLOC(rbio->bio_sectors, num_sectors);
        CONSUME_ALLOC(rbio->stripe_sectors, num_sectors);
        CONSUME_ALLOC(rbio->finish_pointers, real_stripes);
        CONSUME_ALLOC(rbio->dbitmap, BITS_TO_LONGS(stripe_nsectors));
@@ -1166,6 +1189,32 @@ static void validate_rbio_for_rmw(struct btrfs_raid_bio *rbio)
        }
 }
 
+static void index_one_bio(struct btrfs_raid_bio *rbio, struct bio *bio)
+{
+       const u32 sectorsize = rbio->bioc->fs_info->sectorsize;
+       struct bio_vec bvec;
+       struct bvec_iter iter;
+       u32 offset = (bio->bi_iter.bi_sector << SECTOR_SHIFT) -
+                    rbio->bioc->raid_map[0];
+
+       if (bio_flagged(bio, BIO_CLONED))
+               bio->bi_iter = btrfs_bio(bio)->iter;
+
+       bio_for_each_segment(bvec, bio, iter) {
+               u32 bvec_offset;
+
+               for (bvec_offset = 0; bvec_offset < bvec.bv_len;
+                    bvec_offset += sectorsize, offset += sectorsize) {
+                       int index = offset / sectorsize;
+                       struct sector_ptr *sector = &rbio->bio_sectors[index];
+
+                       sector->page = bvec.bv_page;
+                       sector->pgoff = bvec.bv_offset + bvec_offset;
+                       ASSERT(sector->pgoff < PAGE_SIZE);
+               }
+       }
+}
+
 /*
  * helper function to walk our bio list and populate the bio_pages array with
  * the result.  This seems expensive, but it is faster than constantly
@@ -1196,6 +1245,10 @@ static void index_rbio_pages(struct btrfs_raid_bio *rbio)
                        i++;
                }
        }
+       /* This loop will replace above loop when the full switch is done */
+       bio_list_for_each(bio, &rbio->bio_list)
+               index_one_bio(rbio, bio);
+
        spin_unlock_irq(&rbio->bio_list_lock);
 }