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

btrfs: subpage: make reader lock utilize bitmap



Currently btrfs_subpage utilizes its atomic member @reader to manage the
reader counter.  However it is only utilized to prevent the page to be
released/unlocked when we still have reads underway.

In that use case, we don't really allow multiple readers on the same
subpage sector.  So here we can introduce a new locked bitmap to
represent exactly which subpage range is locked for read.

In theory we can remove btrfs_subpage::reader as it's just the set bits
of the new locked bitmap.  But unfortunately bitmap doesn't provide such
handy API yet, so we still keep the reader counter.

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 621b9ff1
Loading
Loading
Loading
Loading
+35 −10
Original line number Diff line number Diff line
@@ -111,6 +111,9 @@ void btrfs_init_subpage_info(struct btrfs_subpage_info *subpage_info, u32 sector
	subpage_info->checked_offset = cur;
	cur += nr_bits;

	subpage_info->locked_offset = cur;
	cur += nr_bits;

	subpage_info->total_nr_bits = cur;
}

@@ -237,28 +240,58 @@ static void btrfs_subpage_assert(const struct btrfs_fs_info *fs_info,
		       start + len <= folio_pos(folio) + PAGE_SIZE);
}

#define subpage_calc_start_bit(fs_info, folio, name, start, len)	\
({									\
	unsigned int start_bit;						\
									\
	btrfs_subpage_assert(fs_info, folio, start, len);		\
	start_bit = offset_in_page(start) >> fs_info->sectorsize_bits;	\
	start_bit += fs_info->subpage_info->name##_offset;		\
	start_bit;							\
})

void btrfs_subpage_start_reader(const struct btrfs_fs_info *fs_info,
				struct folio *folio, u64 start, u32 len)
{
	struct btrfs_subpage *subpage = folio_get_private(folio);
	const int start_bit = subpage_calc_start_bit(fs_info, folio, locked, start, len);
	const int nbits = len >> fs_info->sectorsize_bits;
	unsigned long flags;


	btrfs_subpage_assert(fs_info, folio, start, len);

	spin_lock_irqsave(&subpage->lock, flags);
	/*
	 * Even though it's just for reading the page, no one should have
	 * locked the subpage range.
	 */
	ASSERT(bitmap_test_range_all_zero(subpage->bitmaps, start_bit, nbits));
	bitmap_set(subpage->bitmaps, start_bit, nbits);
	atomic_add(nbits, &subpage->readers);
	spin_unlock_irqrestore(&subpage->lock, flags);
}

void btrfs_subpage_end_reader(const struct btrfs_fs_info *fs_info,
			      struct folio *folio, u64 start, u32 len)
{
	struct btrfs_subpage *subpage = folio_get_private(folio);
	const int start_bit = subpage_calc_start_bit(fs_info, folio, locked, start, len);
	const int nbits = len >> fs_info->sectorsize_bits;
	unsigned long flags;
	bool is_data;
	bool last;

	btrfs_subpage_assert(fs_info, folio, start, len);
	is_data = is_data_inode(folio->mapping->host);

	spin_lock_irqsave(&subpage->lock, flags);

	/* The range should have already been locked. */
	ASSERT(bitmap_test_range_all_set(subpage->bitmaps, start_bit, nbits));
	ASSERT(atomic_read(&subpage->readers) >= nbits);

	bitmap_clear(subpage->bitmaps, start_bit, nbits);
	last = atomic_sub_and_test(nbits, &subpage->readers);

	/*
@@ -270,6 +303,7 @@ void btrfs_subpage_end_reader(const struct btrfs_fs_info *fs_info,
	 */
	if (is_data && last)
		folio_unlock(folio);
	spin_unlock_irqrestore(&subpage->lock, flags);
}

static void btrfs_subpage_clamp_range(struct folio *folio, u64 *start, u32 *len)
@@ -365,16 +399,6 @@ void btrfs_folio_end_writer_lock(const struct btrfs_fs_info *fs_info,
		folio_unlock(folio);
}

#define subpage_calc_start_bit(fs_info, folio, name, start, len)	\
({									\
	unsigned int start_bit;						\
									\
	btrfs_subpage_assert(fs_info, folio, start, len);		\
	start_bit = offset_in_page(start) >> fs_info->sectorsize_bits;	\
	start_bit += fs_info->subpage_info->name##_offset;		\
	start_bit;							\
})

#define subpage_test_bitmap_all_set(fs_info, subpage, name)		\
	bitmap_test_range_all_set(subpage->bitmaps,			\
			fs_info->subpage_info->name##_offset,		\
@@ -751,6 +775,7 @@ void __cold btrfs_subpage_dump_bitmap(const struct btrfs_fs_info *fs_info,
	GET_SUBPAGE_BITMAP(subpage, subpage_info, writeback, &writeback_bitmap);
	GET_SUBPAGE_BITMAP(subpage, subpage_info, ordered, &ordered_bitmap);
	GET_SUBPAGE_BITMAP(subpage, subpage_info, checked, &checked_bitmap);
	GET_SUBPAGE_BITMAP(subpage, subpage_info, locked, &checked_bitmap);
	spin_unlock_irqrestore(&subpage->lock, flags);

	dump_page(folio_page(folio, 0), "btrfs subpage dump");
+11 −1
Original line number Diff line number Diff line
@@ -33,7 +33,7 @@ struct btrfs_subpage_info {
	unsigned int total_nr_bits;

	/*
	 * *_start indicates where the bitmap starts, the length is always
	 * *_offset indicates where the bitmap starts, the length is always
	 * @bitmap_size, which is calculated from PAGE_SIZE / sectorsize.
	 */
	unsigned int uptodate_offset;
@@ -41,6 +41,16 @@ struct btrfs_subpage_info {
	unsigned int writeback_offset;
	unsigned int ordered_offset;
	unsigned int checked_offset;

	/*
	 * For locked bitmaps, normally it's subpage representation for folio
	 * Locked flag, but metadata is different:
	 *
	 * - Metadata doesn't really lock the folio
	 *   It's just to prevent page::private get cleared before the last
	 *   end_page_read().
	 */
	unsigned int locked_offset;
};

/*