btrfs: factor out allocating an array of pages
authorSweet Tea Dorminy <sweettea-kernel@dorminy.me>
Wed, 30 Mar 2022 20:11:22 +0000 (16:11 -0400)
committerDavid Sterba <dsterba@suse.com>
Mon, 16 May 2022 15:03:11 +0000 (17:03 +0200)
Several functions currently populate an array of page pointers one
allocated page at a time. Factor out the common code so as to allow
improvements to all of the sites at once.

Reviewed-by: Nikolay Borisov <nborisov@suse.com>
Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/check-integrity.c
fs/btrfs/compression.c
fs/btrfs/extent_io.c
fs/btrfs/extent_io.h
fs/btrfs/inode.c
fs/btrfs/raid56.c

index 62eb149..b8f9dfa 100644 (file)
@@ -1552,11 +1552,9 @@ static int btrfsic_read_block(struct btrfsic_state *state,
                return -ENOMEM;
        block_ctx->datav = block_ctx->mem_to_free;
        block_ctx->pagev = (struct page **)(block_ctx->datav + num_pages);
-       for (i = 0; i < num_pages; i++) {
-               block_ctx->pagev[i] = alloc_page(GFP_NOFS);
-               if (!block_ctx->pagev[i])
-                       return -1;
-       }
+       ret = btrfs_alloc_page_array(num_pages, block_ctx->pagev);
+       if (ret)
+               return ret;
 
        dev_bytenr = block_ctx->dev_bytenr;
        for (i = 0; i < num_pages;) {
index 19bf36d..d6b6b6c 100644 (file)
@@ -809,8 +809,6 @@ blk_status_t btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
        struct extent_map_tree *em_tree;
        struct compressed_bio *cb;
        unsigned int compressed_len;
-       unsigned int nr_pages;
-       unsigned int pg_index;
        struct bio *comp_bio = NULL;
        const u64 disk_bytenr = bio->bi_iter.bi_sector << SECTOR_SHIFT;
        u64 cur_disk_byte = disk_bytenr;
@@ -820,7 +818,8 @@ blk_status_t btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
        u64 em_start;
        struct extent_map *em;
        blk_status_t ret;
-       int faili = 0;
+       int ret2;
+       int i;
        u8 *sums;
 
        em_tree = &BTRFS_I(inode)->extent_tree;
@@ -863,24 +862,18 @@ blk_status_t btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
        cb->compress_type = extent_compress_type(bio_flags);
        cb->orig_bio = bio;
 
-       nr_pages = DIV_ROUND_UP(compressed_len, PAGE_SIZE);
-       cb->compressed_pages = kcalloc(nr_pages, sizeof(struct page *),
-                                      GFP_NOFS);
+       cb->nr_pages = DIV_ROUND_UP(compressed_len, PAGE_SIZE);
+       cb->compressed_pages = kcalloc(cb->nr_pages, sizeof(struct page *), GFP_NOFS);
        if (!cb->compressed_pages) {
                ret = BLK_STS_RESOURCE;
-               goto fail1;
+               goto fail;
        }
 
-       for (pg_index = 0; pg_index < nr_pages; pg_index++) {
-               cb->compressed_pages[pg_index] = alloc_page(GFP_NOFS);
-               if (!cb->compressed_pages[pg_index]) {
-                       faili = pg_index - 1;
-                       ret = BLK_STS_RESOURCE;
-                       goto fail2;
-               }
+       ret2 = btrfs_alloc_page_array(cb->nr_pages, cb->compressed_pages);
+       if (ret2) {
+               ret = BLK_STS_RESOURCE;
+               goto fail;
        }
-       faili = nr_pages - 1;
-       cb->nr_pages = nr_pages;
 
        add_ra_bio_pages(inode, em_start + em_len, cb);
 
@@ -957,14 +950,15 @@ blk_status_t btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
        }
        return BLK_STS_OK;
 
-fail2:
-       while (faili >= 0) {
-               __free_page(cb->compressed_pages[faili]);
-               faili--;
+fail:
+       if (cb->compressed_pages) {
+               for (i = 0; i < cb->nr_pages; i++) {
+                       if (cb->compressed_pages[i])
+                               __free_page(cb->compressed_pages[i]);
+               }
        }
 
        kfree(cb->compressed_pages);
-fail1:
        kfree(cb);
 out:
        free_extent_map(em);
index f28f059..e472886 100644 (file)
@@ -3133,6 +3133,34 @@ readpage_ok:
        bio_put(bio);
 }
 
+/**
+ * Populate every free slot in a provided array with pages.
+ *
+ * @nr_pages:   number of pages to allocate
+ * @page_array: the array to fill with pages; any existing non-null entries in
+ *             the array will be skipped
+ *
+ * Return: 0        if all pages were able to be allocated;
+ *         -ENOMEM  otherwise, and the caller is responsible for freeing all
+ *                  non-null page pointers in the array.
+ */
+int btrfs_alloc_page_array(unsigned int nr_pages, struct page **page_array)
+{
+       int i;
+
+       for (i = 0; i < nr_pages; i++) {
+               struct page *page;
+
+               if (page_array[i])
+                       continue;
+               page = alloc_page(GFP_NOFS);
+               if (!page)
+                       return -ENOMEM;
+               page_array[i] = page;
+       }
+       return 0;
+}
+
 /*
  * Initialize the members up to but not including 'bio'. Use after allocating a
  * new bio by bio_alloc_bioset as it does not initialize the bytes outside of
@@ -5912,9 +5940,9 @@ __alloc_extent_buffer(struct btrfs_fs_info *fs_info, u64 start,
 struct extent_buffer *btrfs_clone_extent_buffer(const struct extent_buffer *src)
 {
        int i;
-       struct page *p;
        struct extent_buffer *new;
        int num_pages = num_extent_pages(src);
+       int ret;
 
        new = __alloc_extent_buffer(src->fs_info, src->start, src->len);
        if (new == NULL)
@@ -5927,22 +5955,23 @@ struct extent_buffer *btrfs_clone_extent_buffer(const struct extent_buffer *src)
         */
        set_bit(EXTENT_BUFFER_UNMAPPED, &new->bflags);
 
+       memset(new->pages, 0, sizeof(*new->pages) * num_pages);
+       ret = btrfs_alloc_page_array(num_pages, new->pages);
+       if (ret) {
+               btrfs_release_extent_buffer(new);
+               return NULL;
+       }
+
        for (i = 0; i < num_pages; i++) {
                int ret;
+               struct page *p = new->pages[i];
 
-               p = alloc_page(GFP_NOFS);
-               if (!p) {
-                       btrfs_release_extent_buffer(new);
-                       return NULL;
-               }
                ret = attach_extent_buffer_page(new, p, NULL);
                if (ret < 0) {
-                       put_page(p);
                        btrfs_release_extent_buffer(new);
                        return NULL;
                }
                WARN_ON(PageDirty(p));
-               new->pages[i] = p;
                copy_page(page_address(p), page_address(src->pages[i]));
        }
        set_extent_buffer_uptodate(new);
@@ -5956,31 +5985,36 @@ struct extent_buffer *__alloc_dummy_extent_buffer(struct btrfs_fs_info *fs_info,
        struct extent_buffer *eb;
        int num_pages;
        int i;
+       int ret;
 
        eb = __alloc_extent_buffer(fs_info, start, len);
        if (!eb)
                return NULL;
 
        num_pages = num_extent_pages(eb);
+       ret = btrfs_alloc_page_array(num_pages, eb->pages);
+       if (ret)
+               goto err;
+
        for (i = 0; i < num_pages; i++) {
-               int ret;
+               struct page *p = eb->pages[i];
 
-               eb->pages[i] = alloc_page(GFP_NOFS);
-               if (!eb->pages[i])
-                       goto err;
-               ret = attach_extent_buffer_page(eb, eb->pages[i], NULL);
+               ret = attach_extent_buffer_page(eb, p, NULL);
                if (ret < 0)
                        goto err;
        }
+
        set_extent_buffer_uptodate(eb);
        btrfs_set_header_nritems(eb, 0);
        set_bit(EXTENT_BUFFER_UNMAPPED, &eb->bflags);
 
        return eb;
 err:
-       for (; i > 0; i--) {
-               detach_extent_buffer_page(eb, eb->pages[i - 1]);
-               __free_page(eb->pages[i - 1]);
+       for (i = 0; i < num_pages; i++) {
+               if (eb->pages[i]) {
+                       detach_extent_buffer_page(eb, eb->pages[i]);
+                       __free_page(eb->pages[i]);
+               }
        }
        __free_extent_buffer(eb);
        return NULL;
index 151e9da..1331902 100644 (file)
@@ -277,6 +277,8 @@ void extent_range_redirty_for_io(struct inode *inode, u64 start, u64 end);
 void extent_clear_unlock_delalloc(struct btrfs_inode *inode, u64 start, u64 end,
                                  struct page *locked_page,
                                  u32 bits_to_clear, unsigned long page_ops);
+
+int btrfs_alloc_page_array(unsigned int nr_pages, struct page **page_array);
 struct bio *btrfs_bio_alloc(unsigned int nr_iovecs);
 struct bio *btrfs_bio_clone(struct bio *bio);
 struct bio *btrfs_bio_clone_partial(struct bio *orig, u64 offset, u64 size);
index 5ebc86d..77fd450 100644 (file)
@@ -10461,13 +10461,11 @@ static ssize_t btrfs_encoded_read_regular(struct kiocb *iocb,
        pages = kcalloc(nr_pages, sizeof(struct page *), GFP_NOFS);
        if (!pages)
                return -ENOMEM;
-       for (i = 0; i < nr_pages; i++) {
-               pages[i] = alloc_page(GFP_NOFS);
-               if (!pages[i]) {
-                       ret = -ENOMEM;
-                       goto out;
+       ret = btrfs_alloc_page_array(nr_pages, pages);
+       if (ret) {
+               ret = -ENOMEM;
+               goto out;
                }
-       }
 
        ret = btrfs_encoded_read_regular_fill_pages(inode, start, disk_bytenr,
                                                    disk_io_size, pages);
index 0e239a4..ba6f6be 100644 (file)
@@ -1026,37 +1026,16 @@ static struct btrfs_raid_bio *alloc_rbio(struct btrfs_fs_info *fs_info,
 /* allocate pages for all the stripes in the bio, including parity */
 static int alloc_rbio_pages(struct btrfs_raid_bio *rbio)
 {
-       int i;
-       struct page *page;
-
-       for (i = 0; i < rbio->nr_pages; i++) {
-               if (rbio->stripe_pages[i])
-                       continue;
-               page = alloc_page(GFP_NOFS);
-               if (!page)
-                       return -ENOMEM;
-               rbio->stripe_pages[i] = page;
-       }
-       return 0;
+       return btrfs_alloc_page_array(rbio->nr_pages, rbio->stripe_pages);
 }
 
 /* only allocate pages for p/q stripes */
 static int alloc_rbio_parity_pages(struct btrfs_raid_bio *rbio)
 {
-       int i;
-       struct page *page;
-
-       i = rbio_stripe_page_index(rbio, rbio->nr_data, 0);
+       int data_pages = rbio_stripe_page_index(rbio, rbio->nr_data, 0);
 
-       for (; i < rbio->nr_pages; i++) {
-               if (rbio->stripe_pages[i])
-                       continue;
-               page = alloc_page(GFP_NOFS);
-               if (!page)
-                       return -ENOMEM;
-               rbio->stripe_pages[i] = page;
-       }
-       return 0;
+       return btrfs_alloc_page_array(rbio->nr_pages - data_pages,
+                                     rbio->stripe_pages + data_pages);
 }
 
 /*