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

btrfs: subpage: add bitmap for PageChecked flag



Although in btrfs we have very limited usage of PageChecked flag, it's
still some page flag not yet subpage compatible.

Fix it by introducing btrfs_subpage::checked_offset to do the convert.

For most call sites, especially for free-space cache, COW fixup and
btrfs_invalidatepage(), they all work in full page mode anyway.

For other call sites, they work as subpage compatible mode.

Some call sites need extra modification:

- btrfs_drop_pages()
  Needs extra parameter to get the real range we need to clear checked
  flag.

  Also since btrfs_drop_pages() will accept pages beyond the dirtied
  range, update btrfs_subpage_clamp_range() to handle such case
  by setting @len to 0 if the page is beyond target range.

- btrfs_invalidatepage()
  We need to call subpage helper before calling __btrfs_releasepage(),
  or it will trigger ASSERT() as page->private will be cleared.

- btrfs_verify_data_csum()
  In theory we don't need the io_bio->csum check anymore, but it's
  won't hurt.  Just change the comment.

Signed-off-by: default avatarQu Wenruo <wqu@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 6ec9765d
Loading
Loading
Loading
Loading
+8 −2
Original line number Diff line number Diff line
@@ -296,8 +296,14 @@ static void end_compressed_bio_read(struct bio *bio)
		 * checked so the end_io handlers know about it
		 */
		ASSERT(!bio_flagged(bio, BIO_CLONED));
		bio_for_each_segment_all(bvec, cb->orig_bio, iter_all)
			SetPageChecked(bvec->bv_page);
		bio_for_each_segment_all(bvec, cb->orig_bio, iter_all) {
			u64 bvec_start = page_offset(bvec->bv_page) +
					 bvec->bv_offset;

			btrfs_page_set_checked(btrfs_sb(cb->inode->i_sb),
					bvec->bv_page, bvec_start,
					bvec->bv_len);
		}

		bio_endio(cb->orig_bio);
	}
+12 −5
Original line number Diff line number Diff line
@@ -437,9 +437,15 @@ static noinline int btrfs_copy_from_user(loff_t pos, size_t write_bytes,
/*
 * unlocks pages after btrfs_file_write is done with them
 */
static void btrfs_drop_pages(struct page **pages, size_t num_pages)
static void btrfs_drop_pages(struct btrfs_fs_info *fs_info,
			     struct page **pages, size_t num_pages,
			     u64 pos, u64 copied)
{
	size_t i;
	u64 block_start = round_down(pos, fs_info->sectorsize);
	u64 block_len = round_up(pos + copied, fs_info->sectorsize) - block_start;

	ASSERT(block_len <= U32_MAX);
	for (i = 0; i < num_pages; i++) {
		/* page checked is some magic around finding pages that
		 * have been modified without going through btrfs_set_page_dirty
@@ -447,7 +453,8 @@ static void btrfs_drop_pages(struct page **pages, size_t num_pages)
		 * accessed as prepare_pages should have marked them accessed
		 * in prepare_pages via find_or_create_page()
		 */
		ClearPageChecked(pages[i]);
		btrfs_page_clamp_clear_checked(fs_info, pages[i], block_start,
					       block_len);
		unlock_page(pages[i]);
		put_page(pages[i]);
	}
@@ -504,7 +511,7 @@ int btrfs_dirty_pages(struct btrfs_inode *inode, struct page **pages,
		struct page *p = pages[i];

		btrfs_page_clamp_set_uptodate(fs_info, p, start_pos, num_bytes);
		ClearPageChecked(p);
		btrfs_page_clamp_clear_checked(fs_info, p, start_pos, num_bytes);
		btrfs_page_clamp_set_dirty(fs_info, p, start_pos, num_bytes);
	}

@@ -1843,7 +1850,7 @@ static noinline ssize_t btrfs_buffered_write(struct kiocb *iocb,

		btrfs_delalloc_release_extents(BTRFS_I(inode), reserve_bytes);
		if (ret) {
			btrfs_drop_pages(pages, num_pages);
			btrfs_drop_pages(fs_info, pages, num_pages, pos, copied);
			break;
		}

@@ -1851,7 +1858,7 @@ static noinline ssize_t btrfs_buffered_write(struct kiocb *iocb,
		if (only_release_metadata)
			btrfs_check_nocow_unlock(BTRFS_I(inode));

		btrfs_drop_pages(pages, num_pages);
		btrfs_drop_pages(fs_info, pages, num_pages, pos, copied);

		cond_resched();

+5 −1
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
#include "delalloc-space.h"
#include "block-group.h"
#include "discard.h"
#include "subpage.h"

#define BITS_PER_BITMAP		(PAGE_SIZE * 8UL)
#define MAX_CACHE_BYTES_PER_GIG	SZ_64K
@@ -411,7 +412,10 @@ static void io_ctl_drop_pages(struct btrfs_io_ctl *io_ctl)

	for (i = 0; i < io_ctl->num_pages; i++) {
		if (io_ctl->pages[i]) {
			ClearPageChecked(io_ctl->pages[i]);
			btrfs_page_clear_checked(io_ctl->fs_info,
					io_ctl->pages[i],
					page_offset(io_ctl->pages[i]),
					PAGE_SIZE);
			unlock_page(io_ctl->pages[i]);
			put_page(io_ctl->pages[i]);
		}
+12 −16
Original line number Diff line number Diff line
@@ -2764,7 +2764,7 @@ static void btrfs_writepage_fixup_worker(struct btrfs_work *work)
		clear_page_dirty_for_io(page);
		SetPageError(page);
	}
	ClearPageChecked(page);
	btrfs_page_clear_checked(inode->root->fs_info, page, page_start, PAGE_SIZE);
	unlock_page(page);
	put_page(page);
	kfree(fixup);
@@ -2819,7 +2819,7 @@ int btrfs_writepage_cow_fixup(struct page *page)
	 * page->mapping outside of the page lock.
	 */
	ihold(inode);
	SetPageChecked(page);
	btrfs_page_set_checked(fs_info, page, page_offset(page), PAGE_SIZE);
	get_page(page);
	btrfs_init_work(&fixup->work, btrfs_writepage_fixup_worker, NULL, NULL);
	fixup->page = page;
@@ -3269,27 +3269,22 @@ unsigned int btrfs_verify_data_csum(struct btrfs_bio *bbio,
				    u64 start, u64 end)
{
	struct inode *inode = page->mapping->host;
	struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
	struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
	struct btrfs_root *root = BTRFS_I(inode)->root;
	const u32 sectorsize = root->fs_info->sectorsize;
	u32 pg_off;
	unsigned int result = 0;

	if (PageChecked(page)) {
		ClearPageChecked(page);
	if (btrfs_page_test_checked(fs_info, page, start, end + 1 - start)) {
		btrfs_page_clear_checked(fs_info, page, start, end + 1 - start);
		return 0;
	}

	/*
	 * For subpage case, above PageChecked is not safe as it's not subpage
	 * compatible.
	 * But for now only cow fixup and compressed read utilize PageChecked
	 * flag, while in this context we can easily use bbio->csum to
	 * determine if we really need to do csum verification.
	 *
	 * So for now, just exit if bbio->csum is NULL, as it means it's
	 * compressed read, and its compressed data csum has already been
	 * verified.
	 * This only happens for NODATASUM or compressed read.
	 * Normally this should be covered by above check for compressed read
	 * or the next check for NODATASUM.  Just do a quicker exit here.
	 */
	if (bbio->csum == NULL)
		return 0;
@@ -5110,7 +5105,8 @@ int btrfs_truncate_block(struct btrfs_inode *inode, loff_t from, loff_t len,
				     len);
		flush_dcache_page(page);
	}
	ClearPageChecked(page);
	btrfs_page_clear_checked(fs_info, page, block_start,
				 block_end + 1 - block_start);
	btrfs_page_set_dirty(fs_info, page, block_start, block_end + 1 - block_start);
	unlock_extent_cached(io_tree, block_start, block_end, &cached_state);

@@ -8705,9 +8701,9 @@ static void btrfs_invalidatepage(struct page *page, unsigned int offset,
	 * did something wrong.
	 */
	ASSERT(!PageOrdered(page));
	btrfs_page_clear_checked(fs_info, page, page_offset(page), PAGE_SIZE);
	if (!inode_evicting)
		__btrfs_releasepage(page, GFP_NOFS);
	ClearPageChecked(page);
	clear_page_extent_mapped(page);
}

@@ -8851,7 +8847,7 @@ vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
		memzero_page(page, zero_start, PAGE_SIZE - zero_start);
		flush_dcache_page(page);
	}
	ClearPageChecked(page);
	btrfs_page_clear_checked(fs_info, page, page_start, PAGE_SIZE);
	btrfs_page_set_dirty(fs_info, page, page_start, end + 1 - page_start);
	btrfs_page_set_uptodate(fs_info, page, page_start, end + 1 - page_start);

+1 −1
Original line number Diff line number Diff line
@@ -138,7 +138,7 @@ static int copy_inline_to_page(struct btrfs_inode *inode,
	}

	btrfs_page_set_uptodate(fs_info, page, file_offset, block_size);
	ClearPageChecked(page);
	btrfs_page_clear_checked(fs_info, page, file_offset, block_size);
	btrfs_page_set_dirty(fs_info, page, file_offset, block_size);
out_unlock:
	if (page) {
Loading