Skip to content

Commit

Permalink
btrfs: factor out allocating an array of pages
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Sweet Tea Dorminy authored and David Sterba committed May 16, 2022
1 parent 0d031dc commit dd137dd
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 73 deletions.
8 changes: 3 additions & 5 deletions fs/btrfs/check-integrity.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;) {
Expand Down
36 changes: 15 additions & 21 deletions fs/btrfs/compression.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);
Expand Down
66 changes: 50 additions & 16 deletions fs/btrfs/extent_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -3133,6 +3133,34 @@ static void end_bio_extent_readpage(struct bio *bio)
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
Expand Down Expand Up @@ -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)
Expand All @@ -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);
Expand All @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions fs/btrfs/extent_io.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
10 changes: 4 additions & 6 deletions fs/btrfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
29 changes: 4 additions & 25 deletions fs/btrfs/raid56.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/*
Expand Down

0 comments on commit dd137dd

Please sign in to comment.