btrfs: make process_one_page() to handle subpage locking
Introduce a new data inodes specific subpage member, writers, to record how many sectors are under page lock for delalloc writing. This member acts pretty much the same as readers, except it's only for delalloc writes. This is important for delalloc code to trace which page can really be freed, as we have cases like run_delalloc_nocow() where we may exit processing nocow range inside a page, but need to exit to do cow half way. In that case, we need a way to determine if we can really unlock a full page. With the new btrfs_subpage::writers, there is a new requirement: - Page locked by process_one_page() must be unlocked by process_one_page() There are still tons of call sites manually lock and unlock a page, without updating btrfs_subpage::writers. So if we lock a page through process_one_page() then it must be unlocked by process_one_page() to keep btrfs_subpage::writers consistent. This will be handled in next patch. Tested-by: Ritesh Harjani <riteshh@linux.ibm.com> # [ppc64] Tested-by: Anand Jain <anand.jain@oracle.com> # [aarch64] Reviewed-by: Josef Bacik <josef@toxicpanda.com> Signed-off-by: Qu Wenruo <wqu@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
parent
9047e3170a
commit
1e1de38792
@ -1841,14 +1841,18 @@ static int process_one_page(struct btrfs_fs_info *fs_info,
|
|||||||
if (page_ops & PAGE_END_WRITEBACK)
|
if (page_ops & PAGE_END_WRITEBACK)
|
||||||
btrfs_page_clamp_clear_writeback(fs_info, page, start, len);
|
btrfs_page_clamp_clear_writeback(fs_info, page, start, len);
|
||||||
if (page_ops & PAGE_LOCK) {
|
if (page_ops & PAGE_LOCK) {
|
||||||
lock_page(page);
|
int ret;
|
||||||
|
|
||||||
|
ret = btrfs_page_start_writer_lock(fs_info, page, start, len);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
if (!PageDirty(page) || page->mapping != mapping) {
|
if (!PageDirty(page) || page->mapping != mapping) {
|
||||||
unlock_page(page);
|
btrfs_page_end_writer_lock(fs_info, page, start, len);
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (page_ops & PAGE_UNLOCK)
|
if (page_ops & PAGE_UNLOCK)
|
||||||
unlock_page(page);
|
btrfs_page_end_writer_lock(fs_info, page, start, len);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,10 +110,12 @@ int btrfs_alloc_subpage(const struct btrfs_fs_info *fs_info,
|
|||||||
if (!*ret)
|
if (!*ret)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
spin_lock_init(&(*ret)->lock);
|
spin_lock_init(&(*ret)->lock);
|
||||||
if (type == BTRFS_SUBPAGE_METADATA)
|
if (type == BTRFS_SUBPAGE_METADATA) {
|
||||||
atomic_set(&(*ret)->eb_refs, 0);
|
atomic_set(&(*ret)->eb_refs, 0);
|
||||||
else
|
} else {
|
||||||
atomic_set(&(*ret)->readers, 0);
|
atomic_set(&(*ret)->readers, 0);
|
||||||
|
atomic_set(&(*ret)->writers, 0);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,6 +205,79 @@ void btrfs_subpage_end_reader(const struct btrfs_fs_info *fs_info,
|
|||||||
unlock_page(page);
|
unlock_page(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void btrfs_subpage_clamp_range(struct page *page, u64 *start, u32 *len)
|
||||||
|
{
|
||||||
|
u64 orig_start = *start;
|
||||||
|
u32 orig_len = *len;
|
||||||
|
|
||||||
|
*start = max_t(u64, page_offset(page), orig_start);
|
||||||
|
*len = min_t(u64, page_offset(page) + PAGE_SIZE,
|
||||||
|
orig_start + orig_len) - *start;
|
||||||
|
}
|
||||||
|
|
||||||
|
void btrfs_subpage_start_writer(const struct btrfs_fs_info *fs_info,
|
||||||
|
struct page *page, u64 start, u32 len)
|
||||||
|
{
|
||||||
|
struct btrfs_subpage *subpage = (struct btrfs_subpage *)page->private;
|
||||||
|
const int nbits = (len >> fs_info->sectorsize_bits);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
btrfs_subpage_assert(fs_info, page, start, len);
|
||||||
|
|
||||||
|
ASSERT(atomic_read(&subpage->readers) == 0);
|
||||||
|
ret = atomic_add_return(nbits, &subpage->writers);
|
||||||
|
ASSERT(ret == nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool btrfs_subpage_end_and_test_writer(const struct btrfs_fs_info *fs_info,
|
||||||
|
struct page *page, u64 start, u32 len)
|
||||||
|
{
|
||||||
|
struct btrfs_subpage *subpage = (struct btrfs_subpage *)page->private;
|
||||||
|
const int nbits = (len >> fs_info->sectorsize_bits);
|
||||||
|
|
||||||
|
btrfs_subpage_assert(fs_info, page, start, len);
|
||||||
|
|
||||||
|
ASSERT(atomic_read(&subpage->writers) >= nbits);
|
||||||
|
return atomic_sub_and_test(nbits, &subpage->writers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Lock a page for delalloc page writeback.
|
||||||
|
*
|
||||||
|
* Return -EAGAIN if the page is not properly initialized.
|
||||||
|
* Return 0 with the page locked, and writer counter updated.
|
||||||
|
*
|
||||||
|
* Even with 0 returned, the page still need extra check to make sure
|
||||||
|
* it's really the correct page, as the caller is using
|
||||||
|
* find_get_pages_contig(), which can race with page invalidating.
|
||||||
|
*/
|
||||||
|
int btrfs_page_start_writer_lock(const struct btrfs_fs_info *fs_info,
|
||||||
|
struct page *page, u64 start, u32 len)
|
||||||
|
{
|
||||||
|
if (unlikely(!fs_info) || fs_info->sectorsize == PAGE_SIZE) {
|
||||||
|
lock_page(page);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
lock_page(page);
|
||||||
|
if (!PagePrivate(page) || !page->private) {
|
||||||
|
unlock_page(page);
|
||||||
|
return -EAGAIN;
|
||||||
|
}
|
||||||
|
btrfs_subpage_clamp_range(page, &start, &len);
|
||||||
|
btrfs_subpage_start_writer(fs_info, page, start, len);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void btrfs_page_end_writer_lock(const struct btrfs_fs_info *fs_info,
|
||||||
|
struct page *page, u64 start, u32 len)
|
||||||
|
{
|
||||||
|
if (unlikely(!fs_info) || fs_info->sectorsize == PAGE_SIZE)
|
||||||
|
return unlock_page(page);
|
||||||
|
btrfs_subpage_clamp_range(page, &start, &len);
|
||||||
|
if (btrfs_subpage_end_and_test_writer(fs_info, page, start, len))
|
||||||
|
unlock_page(page);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert the [start, start + len) range into a u16 bitmap
|
* Convert the [start, start + len) range into a u16 bitmap
|
||||||
*
|
*
|
||||||
@ -354,16 +429,6 @@ void btrfs_subpage_clear_writeback(const struct btrfs_fs_info *fs_info,
|
|||||||
spin_unlock_irqrestore(&subpage->lock, flags);
|
spin_unlock_irqrestore(&subpage->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void btrfs_subpage_clamp_range(struct page *page, u64 *start, u32 *len)
|
|
||||||
{
|
|
||||||
u64 orig_start = *start;
|
|
||||||
u32 orig_len = *len;
|
|
||||||
|
|
||||||
*start = max_t(u64, page_offset(page), orig_start);
|
|
||||||
*len = min_t(u64, page_offset(page) + PAGE_SIZE,
|
|
||||||
orig_start + orig_len) - *start;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Unlike set/clear which is dependent on each page status, for test all bits
|
* Unlike set/clear which is dependent on each page status, for test all bits
|
||||||
* are tested in the same way.
|
* are tested in the same way.
|
||||||
|
@ -33,6 +33,7 @@ struct btrfs_subpage {
|
|||||||
/* Structures only used by data */
|
/* Structures only used by data */
|
||||||
struct {
|
struct {
|
||||||
atomic_t readers;
|
atomic_t readers;
|
||||||
|
atomic_t writers;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -63,6 +64,15 @@ void btrfs_subpage_start_reader(const struct btrfs_fs_info *fs_info,
|
|||||||
void btrfs_subpage_end_reader(const struct btrfs_fs_info *fs_info,
|
void btrfs_subpage_end_reader(const struct btrfs_fs_info *fs_info,
|
||||||
struct page *page, u64 start, u32 len);
|
struct page *page, u64 start, u32 len);
|
||||||
|
|
||||||
|
void btrfs_subpage_start_writer(const struct btrfs_fs_info *fs_info,
|
||||||
|
struct page *page, u64 start, u32 len);
|
||||||
|
bool btrfs_subpage_end_and_test_writer(const struct btrfs_fs_info *fs_info,
|
||||||
|
struct page *page, u64 start, u32 len);
|
||||||
|
int btrfs_page_start_writer_lock(const struct btrfs_fs_info *fs_info,
|
||||||
|
struct page *page, u64 start, u32 len);
|
||||||
|
void btrfs_page_end_writer_lock(const struct btrfs_fs_info *fs_info,
|
||||||
|
struct page *page, u64 start, u32 len);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Template for subpage related operations.
|
* Template for subpage related operations.
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user