Commit c2ffb1ec authored by Qu Wenruo's avatar Qu Wenruo Committed by David Sterba
Browse files

btrfs: prepare compression folio alloc/free for bs > ps cases



This includes the following preparation for bs > ps cases:

- Always alloc/free the folio directly if bs > ps
  This adds a new @fs_info parameter for btrfs_alloc_compr_folio(), thus
  affecting all compression algorithms.

  For btrfs_free_compr_folio() it needs no parameter for now, as we can
  use the folio size to skip the caching part.

  For now the change is just to passing a @fs_info into the function,
  all the folio size assumption is still based on page size.

- Properly zero the last folio in compress_file_range()
  Since the compressed folios can be larger than a page, we need to
  properly zero the whole folio.

- Use correct folio size for btrfs_add_compressed_bio_folios()
  Instead of page size, use the correct folio size.

- Use correct folio size/shift for btrfs_compress_filemap_get_folio()
  As we are not only using simple page sized folios anymore.

- Use correct folio size for btrfs_decompress()
  There is an ASSERT() making sure the decompressed range is no larger
  than a page, which will be triggered for bs > ps cases.

- Skip readahead for compressed pages
  Similar to subpage cases.

- Make btrfs_alloc_folio_array() to accept a new @order parameter

- Add a helper to calculate the minimal folio size

All those changes should not affect the existing bs <= ps handling.

Signed-off-by: default avatarQu Wenruo <wqu@suse.com>
Reviewed-by: default avatarDavid Sterba <dsterba@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 7b26da40
Loading
Loading
Loading
Loading
+30 −12
Original line number Diff line number Diff line
@@ -223,10 +223,14 @@ static unsigned long btrfs_compr_pool_scan(struct shrinker *sh, struct shrink_co
/*
 * Common wrappers for page allocation from compression wrappers
 */
struct folio *btrfs_alloc_compr_folio(void)
struct folio *btrfs_alloc_compr_folio(struct btrfs_fs_info *fs_info)
{
	struct folio *folio = NULL;

	/* For bs > ps cases, no cached folio pool for now. */
	if (fs_info->block_min_order)
		goto alloc;

	spin_lock(&compr_pool.lock);
	if (compr_pool.count > 0) {
		folio = list_first_entry(&compr_pool.list, struct folio, lru);
@@ -238,13 +242,18 @@ struct folio *btrfs_alloc_compr_folio(void)
	if (folio)
		return folio;

	return folio_alloc(GFP_NOFS, 0);
alloc:
	return folio_alloc(GFP_NOFS, fs_info->block_min_order);
}

void btrfs_free_compr_folio(struct folio *folio)
{
	bool do_free = false;

	/* The folio is from bs > ps fs, no cached pool for now. */
	if (folio_order(folio))
		goto free;

	spin_lock(&compr_pool.lock);
	if (compr_pool.count > compr_pool.thresh) {
		do_free = true;
@@ -257,6 +266,7 @@ void btrfs_free_compr_folio(struct folio *folio)
	if (!do_free)
		return;

free:
	ASSERT(folio_ref_count(folio) == 1);
	folio_put(folio);
}
@@ -344,16 +354,19 @@ static void end_bbio_compressed_write(struct btrfs_bio *bbio)

static void btrfs_add_compressed_bio_folios(struct compressed_bio *cb)
{
	struct btrfs_fs_info *fs_info = cb->bbio.fs_info;
	struct bio *bio = &cb->bbio.bio;
	u32 offset = 0;

	while (offset < cb->compressed_len) {
		struct folio *folio;
		int ret;
		u32 len = min_t(u32, cb->compressed_len - offset, PAGE_SIZE);
		u32 len = min_t(u32, cb->compressed_len - offset,
				btrfs_min_folio_size(fs_info));

		folio = cb->compressed_folios[offset >> (PAGE_SHIFT + fs_info->block_min_order)];
		/* Maximum compressed extent is smaller than bio size limit. */
		ret = bio_add_folio(bio, cb->compressed_folios[offset >> PAGE_SHIFT],
				    len, 0);
		ret = bio_add_folio(bio, folio, len, 0);
		ASSERT(ret);
		offset += len;
	}
@@ -443,6 +456,10 @@ static noinline int add_ra_bio_pages(struct inode *inode,
	if (fs_info->sectorsize < PAGE_SIZE)
		return 0;

	/* For bs > ps cases, we don't support readahead for compressed folios for now. */
	if (fs_info->block_min_order)
		return 0;

	end_index = (i_size_read(inode) - 1) >> PAGE_SHIFT;

	while (cur < compressed_end) {
@@ -606,14 +623,15 @@ void btrfs_submit_compressed_read(struct btrfs_bio *bbio)

	btrfs_free_extent_map(em);

	cb->nr_folios = DIV_ROUND_UP(compressed_len, PAGE_SIZE);
	cb->nr_folios = DIV_ROUND_UP(compressed_len, btrfs_min_folio_size(fs_info));
	cb->compressed_folios = kcalloc(cb->nr_folios, sizeof(struct folio *), GFP_NOFS);
	if (!cb->compressed_folios) {
		status = BLK_STS_RESOURCE;
		goto out_free_bio;
	}

	ret = btrfs_alloc_folio_array(cb->nr_folios, cb->compressed_folios);
	ret = btrfs_alloc_folio_array(cb->nr_folios, fs_info->block_min_order,
				      cb->compressed_folios);
	if (ret) {
		status = BLK_STS_RESOURCE;
		goto out_free_compressed_pages;
@@ -1033,12 +1051,12 @@ int btrfs_compress_filemap_get_folio(struct address_space *mapping, u64 start,
 * - compression algo are 0-3
 * - the level are bits 4-7
 *
 * @out_pages is an in/out parameter, holds maximum number of pages to allocate
 * and returns number of actually allocated pages
 * @out_folios is an in/out parameter, holds maximum number of folios to allocate
 * and returns number of actually allocated folios
 *
 * @total_in is used to return the number of bytes actually read.  It
 * may be smaller than the input length if we had to exit early because we
 * ran out of room in the pages array or because we cross the
 * ran out of room in the folios array or because we cross the
 * max_out threshold.
 *
 * @total_out is an in/out parameter, must be set to the input length and will
@@ -1093,11 +1111,11 @@ int btrfs_decompress(int type, const u8 *data_in, struct folio *dest_folio,
	int ret;

	/*
	 * The full destination page range should not exceed the page size.
	 * The full destination folio range should not exceed the folio size.
	 * And the @destlen should not exceed sectorsize, as this is only called for
	 * inline file extents, which should not exceed sectorsize.
	 */
	ASSERT(dest_pgoff + destlen <= PAGE_SIZE && destlen <= sectorsize);
	ASSERT(dest_pgoff + destlen <= folio_size(dest_folio) && destlen <= sectorsize);

	workspace = get_workspace(fs_info, type, 0);
	ret = compression_decompress(type, workspace, data_in, dest_folio,
+1 −1
Original line number Diff line number Diff line
@@ -112,7 +112,7 @@ void btrfs_submit_compressed_read(struct btrfs_bio *bbio);

int btrfs_compress_str2level(unsigned int type, const char *str, int *level_ret);

struct folio *btrfs_alloc_compr_folio(void);
struct folio *btrfs_alloc_compr_folio(struct btrfs_fs_info *fs_info);
void btrfs_free_compr_folio(struct folio *folio);

struct workspace_manager {
+5 −2
Original line number Diff line number Diff line
@@ -626,6 +626,7 @@ static void end_bbio_data_read(struct btrfs_bio *bbio)
 * Populate every free slot in a provided array with folios using GFP_NOFS.
 *
 * @nr_folios:   number of folios to allocate
 * @order:	 the order of the folios to be allocated
 * @folio_array: the array to fill with folios; any existing non-NULL entries in
 *		 the array will be skipped
 *
@@ -633,12 +634,13 @@ static void end_bbio_data_read(struct btrfs_bio *bbio)
 *         -ENOMEM  otherwise, the partially allocated folios would be freed and
 *                  the array slots zeroed
 */
int btrfs_alloc_folio_array(unsigned int nr_folios, struct folio **folio_array)
int btrfs_alloc_folio_array(unsigned int nr_folios, unsigned int order,
			    struct folio **folio_array)
{
	for (int i = 0; i < nr_folios; i++) {
		if (folio_array[i])
			continue;
		folio_array[i] = folio_alloc(GFP_NOFS, 0);
		folio_array[i] = folio_alloc(GFP_NOFS, order);
		if (!folio_array[i])
			goto error;
	}
@@ -647,6 +649,7 @@ int btrfs_alloc_folio_array(unsigned int nr_folios, struct folio **folio_array)
	for (int i = 0; i < nr_folios; i++) {
		if (folio_array[i])
			folio_put(folio_array[i]);
		folio_array[i] = NULL;
	}
	return -ENOMEM;
}
+2 −1
Original line number Diff line number Diff line
@@ -366,7 +366,8 @@ void btrfs_clear_buffer_dirty(struct btrfs_trans_handle *trans,

int btrfs_alloc_page_array(unsigned int nr_pages, struct page **page_array,
			   bool nofail);
int btrfs_alloc_folio_array(unsigned int nr_folios, struct folio **folio_array);
int btrfs_alloc_folio_array(unsigned int nr_folios, unsigned int order,
			    struct folio **folio_array);

#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
bool find_lock_delalloc_range(struct inode *inode,
+6 −0
Original line number Diff line number Diff line
@@ -923,6 +923,12 @@ static inline gfp_t btrfs_alloc_write_mask(struct address_space *mapping)
	return mapping_gfp_constraint(mapping, ~__GFP_FS);
}

/* Return the minimal folio size of the fs. */
static inline unsigned int btrfs_min_folio_size(struct btrfs_fs_info *fs_info)
{
	return 1U << (PAGE_SHIFT + fs_info->block_min_order);
}

static inline u64 btrfs_get_fs_generation(const struct btrfs_fs_info *fs_info)
{
	return READ_ONCE(fs_info->generation);
Loading