162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include <linux/slab.h> 462306a36Sopenharmony_ci#include "messages.h" 562306a36Sopenharmony_ci#include "ctree.h" 662306a36Sopenharmony_ci#include "subpage.h" 762306a36Sopenharmony_ci#include "btrfs_inode.h" 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci/* 1062306a36Sopenharmony_ci * Subpage (sectorsize < PAGE_SIZE) support overview: 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Limitations: 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * - Only support 64K page size for now 1562306a36Sopenharmony_ci * This is to make metadata handling easier, as 64K page would ensure 1662306a36Sopenharmony_ci * all nodesize would fit inside one page, thus we don't need to handle 1762306a36Sopenharmony_ci * cases where a tree block crosses several pages. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * - Only metadata read-write for now 2062306a36Sopenharmony_ci * The data read-write part is in development. 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * - Metadata can't cross 64K page boundary 2362306a36Sopenharmony_ci * btrfs-progs and kernel have done that for a while, thus only ancient 2462306a36Sopenharmony_ci * filesystems could have such problem. For such case, do a graceful 2562306a36Sopenharmony_ci * rejection. 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * Special behavior: 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * - Metadata 3062306a36Sopenharmony_ci * Metadata read is fully supported. 3162306a36Sopenharmony_ci * Meaning when reading one tree block will only trigger the read for the 3262306a36Sopenharmony_ci * needed range, other unrelated range in the same page will not be touched. 3362306a36Sopenharmony_ci * 3462306a36Sopenharmony_ci * Metadata write support is partial. 3562306a36Sopenharmony_ci * The writeback is still for the full page, but we will only submit 3662306a36Sopenharmony_ci * the dirty extent buffers in the page. 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * This means, if we have a metadata page like this: 3962306a36Sopenharmony_ci * 4062306a36Sopenharmony_ci * Page offset 4162306a36Sopenharmony_ci * 0 16K 32K 48K 64K 4262306a36Sopenharmony_ci * |/////////| |///////////| 4362306a36Sopenharmony_ci * \- Tree block A \- Tree block B 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci * Even if we just want to writeback tree block A, we will also writeback 4662306a36Sopenharmony_ci * tree block B if it's also dirty. 4762306a36Sopenharmony_ci * 4862306a36Sopenharmony_ci * This may cause extra metadata writeback which results more COW. 4962306a36Sopenharmony_ci * 5062306a36Sopenharmony_ci * Implementation: 5162306a36Sopenharmony_ci * 5262306a36Sopenharmony_ci * - Common 5362306a36Sopenharmony_ci * Both metadata and data will use a new structure, btrfs_subpage, to 5462306a36Sopenharmony_ci * record the status of each sector inside a page. This provides the extra 5562306a36Sopenharmony_ci * granularity needed. 5662306a36Sopenharmony_ci * 5762306a36Sopenharmony_ci * - Metadata 5862306a36Sopenharmony_ci * Since we have multiple tree blocks inside one page, we can't rely on page 5962306a36Sopenharmony_ci * locking anymore, or we will have greatly reduced concurrency or even 6062306a36Sopenharmony_ci * deadlocks (hold one tree lock while trying to lock another tree lock in 6162306a36Sopenharmony_ci * the same page). 6262306a36Sopenharmony_ci * 6362306a36Sopenharmony_ci * Thus for metadata locking, subpage support relies on io_tree locking only. 6462306a36Sopenharmony_ci * This means a slightly higher tree locking latency. 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cibool btrfs_is_subpage(const struct btrfs_fs_info *fs_info, struct page *page) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci if (fs_info->sectorsize >= PAGE_SIZE) 7062306a36Sopenharmony_ci return false; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* 7362306a36Sopenharmony_ci * Only data pages (either through DIO or compression) can have no 7462306a36Sopenharmony_ci * mapping. And if page->mapping->host is data inode, it's subpage. 7562306a36Sopenharmony_ci * As we have ruled our sectorsize >= PAGE_SIZE case already. 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_ci if (!page->mapping || !page->mapping->host || 7862306a36Sopenharmony_ci is_data_inode(page->mapping->host)) 7962306a36Sopenharmony_ci return true; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci /* 8262306a36Sopenharmony_ci * Now the only remaining case is metadata, which we only go subpage 8362306a36Sopenharmony_ci * routine if nodesize < PAGE_SIZE. 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_ci if (fs_info->nodesize < PAGE_SIZE) 8662306a36Sopenharmony_ci return true; 8762306a36Sopenharmony_ci return false; 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_civoid btrfs_init_subpage_info(struct btrfs_subpage_info *subpage_info, u32 sectorsize) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci unsigned int cur = 0; 9362306a36Sopenharmony_ci unsigned int nr_bits; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci ASSERT(IS_ALIGNED(PAGE_SIZE, sectorsize)); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci nr_bits = PAGE_SIZE / sectorsize; 9862306a36Sopenharmony_ci subpage_info->bitmap_nr_bits = nr_bits; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci subpage_info->uptodate_offset = cur; 10162306a36Sopenharmony_ci cur += nr_bits; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci subpage_info->dirty_offset = cur; 10462306a36Sopenharmony_ci cur += nr_bits; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci subpage_info->writeback_offset = cur; 10762306a36Sopenharmony_ci cur += nr_bits; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci subpage_info->ordered_offset = cur; 11062306a36Sopenharmony_ci cur += nr_bits; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci subpage_info->checked_offset = cur; 11362306a36Sopenharmony_ci cur += nr_bits; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci subpage_info->total_nr_bits = cur; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ciint btrfs_attach_subpage(const struct btrfs_fs_info *fs_info, 11962306a36Sopenharmony_ci struct page *page, enum btrfs_subpage_type type) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct btrfs_subpage *subpage; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* 12462306a36Sopenharmony_ci * We have cases like a dummy extent buffer page, which is not mapped 12562306a36Sopenharmony_ci * and doesn't need to be locked. 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_ci if (page->mapping) 12862306a36Sopenharmony_ci ASSERT(PageLocked(page)); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* Either not subpage, or the page already has private attached */ 13162306a36Sopenharmony_ci if (!btrfs_is_subpage(fs_info, page) || PagePrivate(page)) 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci subpage = btrfs_alloc_subpage(fs_info, type); 13562306a36Sopenharmony_ci if (IS_ERR(subpage)) 13662306a36Sopenharmony_ci return PTR_ERR(subpage); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci attach_page_private(page, subpage); 13962306a36Sopenharmony_ci return 0; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_civoid btrfs_detach_subpage(const struct btrfs_fs_info *fs_info, 14362306a36Sopenharmony_ci struct page *page) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct btrfs_subpage *subpage; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci /* Either not subpage, or already detached */ 14862306a36Sopenharmony_ci if (!btrfs_is_subpage(fs_info, page) || !PagePrivate(page)) 14962306a36Sopenharmony_ci return; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci subpage = detach_page_private(page); 15262306a36Sopenharmony_ci ASSERT(subpage); 15362306a36Sopenharmony_ci btrfs_free_subpage(subpage); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistruct btrfs_subpage *btrfs_alloc_subpage(const struct btrfs_fs_info *fs_info, 15762306a36Sopenharmony_ci enum btrfs_subpage_type type) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct btrfs_subpage *ret; 16062306a36Sopenharmony_ci unsigned int real_size; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci ASSERT(fs_info->sectorsize < PAGE_SIZE); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci real_size = struct_size(ret, bitmaps, 16562306a36Sopenharmony_ci BITS_TO_LONGS(fs_info->subpage_info->total_nr_bits)); 16662306a36Sopenharmony_ci ret = kzalloc(real_size, GFP_NOFS); 16762306a36Sopenharmony_ci if (!ret) 16862306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci spin_lock_init(&ret->lock); 17162306a36Sopenharmony_ci if (type == BTRFS_SUBPAGE_METADATA) { 17262306a36Sopenharmony_ci atomic_set(&ret->eb_refs, 0); 17362306a36Sopenharmony_ci } else { 17462306a36Sopenharmony_ci atomic_set(&ret->readers, 0); 17562306a36Sopenharmony_ci atomic_set(&ret->writers, 0); 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci return ret; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_civoid btrfs_free_subpage(struct btrfs_subpage *subpage) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci kfree(subpage); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci/* 18662306a36Sopenharmony_ci * Increase the eb_refs of current subpage. 18762306a36Sopenharmony_ci * 18862306a36Sopenharmony_ci * This is important for eb allocation, to prevent race with last eb freeing 18962306a36Sopenharmony_ci * of the same page. 19062306a36Sopenharmony_ci * With the eb_refs increased before the eb inserted into radix tree, 19162306a36Sopenharmony_ci * detach_extent_buffer_page() won't detach the page private while we're still 19262306a36Sopenharmony_ci * allocating the extent buffer. 19362306a36Sopenharmony_ci */ 19462306a36Sopenharmony_civoid btrfs_page_inc_eb_refs(const struct btrfs_fs_info *fs_info, 19562306a36Sopenharmony_ci struct page *page) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct btrfs_subpage *subpage; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (!btrfs_is_subpage(fs_info, page)) 20062306a36Sopenharmony_ci return; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci ASSERT(PagePrivate(page) && page->mapping); 20362306a36Sopenharmony_ci lockdep_assert_held(&page->mapping->private_lock); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci subpage = (struct btrfs_subpage *)page->private; 20662306a36Sopenharmony_ci atomic_inc(&subpage->eb_refs); 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_civoid btrfs_page_dec_eb_refs(const struct btrfs_fs_info *fs_info, 21062306a36Sopenharmony_ci struct page *page) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci struct btrfs_subpage *subpage; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (!btrfs_is_subpage(fs_info, page)) 21562306a36Sopenharmony_ci return; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci ASSERT(PagePrivate(page) && page->mapping); 21862306a36Sopenharmony_ci lockdep_assert_held(&page->mapping->private_lock); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci subpage = (struct btrfs_subpage *)page->private; 22162306a36Sopenharmony_ci ASSERT(atomic_read(&subpage->eb_refs)); 22262306a36Sopenharmony_ci atomic_dec(&subpage->eb_refs); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic void btrfs_subpage_assert(const struct btrfs_fs_info *fs_info, 22662306a36Sopenharmony_ci struct page *page, u64 start, u32 len) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci /* Basic checks */ 22962306a36Sopenharmony_ci ASSERT(PagePrivate(page) && page->private); 23062306a36Sopenharmony_ci ASSERT(IS_ALIGNED(start, fs_info->sectorsize) && 23162306a36Sopenharmony_ci IS_ALIGNED(len, fs_info->sectorsize)); 23262306a36Sopenharmony_ci /* 23362306a36Sopenharmony_ci * The range check only works for mapped page, we can still have 23462306a36Sopenharmony_ci * unmapped page like dummy extent buffer pages. 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_ci if (page->mapping) 23762306a36Sopenharmony_ci ASSERT(page_offset(page) <= start && 23862306a36Sopenharmony_ci start + len <= page_offset(page) + PAGE_SIZE); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_civoid btrfs_subpage_start_reader(const struct btrfs_fs_info *fs_info, 24262306a36Sopenharmony_ci struct page *page, u64 start, u32 len) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct btrfs_subpage *subpage = (struct btrfs_subpage *)page->private; 24562306a36Sopenharmony_ci const int nbits = len >> fs_info->sectorsize_bits; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci btrfs_subpage_assert(fs_info, page, start, len); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci atomic_add(nbits, &subpage->readers); 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_civoid btrfs_subpage_end_reader(const struct btrfs_fs_info *fs_info, 25362306a36Sopenharmony_ci struct page *page, u64 start, u32 len) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci struct btrfs_subpage *subpage = (struct btrfs_subpage *)page->private; 25662306a36Sopenharmony_ci const int nbits = len >> fs_info->sectorsize_bits; 25762306a36Sopenharmony_ci bool is_data; 25862306a36Sopenharmony_ci bool last; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci btrfs_subpage_assert(fs_info, page, start, len); 26162306a36Sopenharmony_ci is_data = is_data_inode(page->mapping->host); 26262306a36Sopenharmony_ci ASSERT(atomic_read(&subpage->readers) >= nbits); 26362306a36Sopenharmony_ci last = atomic_sub_and_test(nbits, &subpage->readers); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* 26662306a36Sopenharmony_ci * For data we need to unlock the page if the last read has finished. 26762306a36Sopenharmony_ci * 26862306a36Sopenharmony_ci * And please don't replace @last with atomic_sub_and_test() call 26962306a36Sopenharmony_ci * inside if () condition. 27062306a36Sopenharmony_ci * As we want the atomic_sub_and_test() to be always executed. 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_ci if (is_data && last) 27362306a36Sopenharmony_ci unlock_page(page); 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic void btrfs_subpage_clamp_range(struct page *page, u64 *start, u32 *len) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci u64 orig_start = *start; 27962306a36Sopenharmony_ci u32 orig_len = *len; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci *start = max_t(u64, page_offset(page), orig_start); 28262306a36Sopenharmony_ci /* 28362306a36Sopenharmony_ci * For certain call sites like btrfs_drop_pages(), we may have pages 28462306a36Sopenharmony_ci * beyond the target range. In that case, just set @len to 0, subpage 28562306a36Sopenharmony_ci * helpers can handle @len == 0 without any problem. 28662306a36Sopenharmony_ci */ 28762306a36Sopenharmony_ci if (page_offset(page) >= orig_start + orig_len) 28862306a36Sopenharmony_ci *len = 0; 28962306a36Sopenharmony_ci else 29062306a36Sopenharmony_ci *len = min_t(u64, page_offset(page) + PAGE_SIZE, 29162306a36Sopenharmony_ci orig_start + orig_len) - *start; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_civoid btrfs_subpage_start_writer(const struct btrfs_fs_info *fs_info, 29562306a36Sopenharmony_ci struct page *page, u64 start, u32 len) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci struct btrfs_subpage *subpage = (struct btrfs_subpage *)page->private; 29862306a36Sopenharmony_ci const int nbits = (len >> fs_info->sectorsize_bits); 29962306a36Sopenharmony_ci int ret; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci btrfs_subpage_assert(fs_info, page, start, len); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci ASSERT(atomic_read(&subpage->readers) == 0); 30462306a36Sopenharmony_ci ret = atomic_add_return(nbits, &subpage->writers); 30562306a36Sopenharmony_ci ASSERT(ret == nbits); 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cibool btrfs_subpage_end_and_test_writer(const struct btrfs_fs_info *fs_info, 30962306a36Sopenharmony_ci struct page *page, u64 start, u32 len) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct btrfs_subpage *subpage = (struct btrfs_subpage *)page->private; 31262306a36Sopenharmony_ci const int nbits = (len >> fs_info->sectorsize_bits); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci btrfs_subpage_assert(fs_info, page, start, len); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci /* 31762306a36Sopenharmony_ci * We have call sites passing @lock_page into 31862306a36Sopenharmony_ci * extent_clear_unlock_delalloc() for compression path. 31962306a36Sopenharmony_ci * 32062306a36Sopenharmony_ci * This @locked_page is locked by plain lock_page(), thus its 32162306a36Sopenharmony_ci * subpage::writers is 0. Handle them in a special way. 32262306a36Sopenharmony_ci */ 32362306a36Sopenharmony_ci if (atomic_read(&subpage->writers) == 0) 32462306a36Sopenharmony_ci return true; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci ASSERT(atomic_read(&subpage->writers) >= nbits); 32762306a36Sopenharmony_ci return atomic_sub_and_test(nbits, &subpage->writers); 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci/* 33162306a36Sopenharmony_ci * Lock a page for delalloc page writeback. 33262306a36Sopenharmony_ci * 33362306a36Sopenharmony_ci * Return -EAGAIN if the page is not properly initialized. 33462306a36Sopenharmony_ci * Return 0 with the page locked, and writer counter updated. 33562306a36Sopenharmony_ci * 33662306a36Sopenharmony_ci * Even with 0 returned, the page still need extra check to make sure 33762306a36Sopenharmony_ci * it's really the correct page, as the caller is using 33862306a36Sopenharmony_ci * filemap_get_folios_contig(), which can race with page invalidating. 33962306a36Sopenharmony_ci */ 34062306a36Sopenharmony_ciint btrfs_page_start_writer_lock(const struct btrfs_fs_info *fs_info, 34162306a36Sopenharmony_ci struct page *page, u64 start, u32 len) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci if (unlikely(!fs_info) || !btrfs_is_subpage(fs_info, page)) { 34462306a36Sopenharmony_ci lock_page(page); 34562306a36Sopenharmony_ci return 0; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci lock_page(page); 34862306a36Sopenharmony_ci if (!PagePrivate(page) || !page->private) { 34962306a36Sopenharmony_ci unlock_page(page); 35062306a36Sopenharmony_ci return -EAGAIN; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci btrfs_subpage_clamp_range(page, &start, &len); 35362306a36Sopenharmony_ci btrfs_subpage_start_writer(fs_info, page, start, len); 35462306a36Sopenharmony_ci return 0; 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_civoid btrfs_page_end_writer_lock(const struct btrfs_fs_info *fs_info, 35862306a36Sopenharmony_ci struct page *page, u64 start, u32 len) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci if (unlikely(!fs_info) || !btrfs_is_subpage(fs_info, page)) 36162306a36Sopenharmony_ci return unlock_page(page); 36262306a36Sopenharmony_ci btrfs_subpage_clamp_range(page, &start, &len); 36362306a36Sopenharmony_ci if (btrfs_subpage_end_and_test_writer(fs_info, page, start, len)) 36462306a36Sopenharmony_ci unlock_page(page); 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci#define subpage_calc_start_bit(fs_info, page, name, start, len) \ 36862306a36Sopenharmony_ci({ \ 36962306a36Sopenharmony_ci unsigned int start_bit; \ 37062306a36Sopenharmony_ci \ 37162306a36Sopenharmony_ci btrfs_subpage_assert(fs_info, page, start, len); \ 37262306a36Sopenharmony_ci start_bit = offset_in_page(start) >> fs_info->sectorsize_bits; \ 37362306a36Sopenharmony_ci start_bit += fs_info->subpage_info->name##_offset; \ 37462306a36Sopenharmony_ci start_bit; \ 37562306a36Sopenharmony_ci}) 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci#define subpage_test_bitmap_all_set(fs_info, subpage, name) \ 37862306a36Sopenharmony_ci bitmap_test_range_all_set(subpage->bitmaps, \ 37962306a36Sopenharmony_ci fs_info->subpage_info->name##_offset, \ 38062306a36Sopenharmony_ci fs_info->subpage_info->bitmap_nr_bits) 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci#define subpage_test_bitmap_all_zero(fs_info, subpage, name) \ 38362306a36Sopenharmony_ci bitmap_test_range_all_zero(subpage->bitmaps, \ 38462306a36Sopenharmony_ci fs_info->subpage_info->name##_offset, \ 38562306a36Sopenharmony_ci fs_info->subpage_info->bitmap_nr_bits) 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_civoid btrfs_subpage_set_uptodate(const struct btrfs_fs_info *fs_info, 38862306a36Sopenharmony_ci struct page *page, u64 start, u32 len) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci struct btrfs_subpage *subpage = (struct btrfs_subpage *)page->private; 39162306a36Sopenharmony_ci unsigned int start_bit = subpage_calc_start_bit(fs_info, page, 39262306a36Sopenharmony_ci uptodate, start, len); 39362306a36Sopenharmony_ci unsigned long flags; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci spin_lock_irqsave(&subpage->lock, flags); 39662306a36Sopenharmony_ci bitmap_set(subpage->bitmaps, start_bit, len >> fs_info->sectorsize_bits); 39762306a36Sopenharmony_ci if (subpage_test_bitmap_all_set(fs_info, subpage, uptodate)) 39862306a36Sopenharmony_ci SetPageUptodate(page); 39962306a36Sopenharmony_ci spin_unlock_irqrestore(&subpage->lock, flags); 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_civoid btrfs_subpage_clear_uptodate(const struct btrfs_fs_info *fs_info, 40362306a36Sopenharmony_ci struct page *page, u64 start, u32 len) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct btrfs_subpage *subpage = (struct btrfs_subpage *)page->private; 40662306a36Sopenharmony_ci unsigned int start_bit = subpage_calc_start_bit(fs_info, page, 40762306a36Sopenharmony_ci uptodate, start, len); 40862306a36Sopenharmony_ci unsigned long flags; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci spin_lock_irqsave(&subpage->lock, flags); 41162306a36Sopenharmony_ci bitmap_clear(subpage->bitmaps, start_bit, len >> fs_info->sectorsize_bits); 41262306a36Sopenharmony_ci ClearPageUptodate(page); 41362306a36Sopenharmony_ci spin_unlock_irqrestore(&subpage->lock, flags); 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_civoid btrfs_subpage_set_dirty(const struct btrfs_fs_info *fs_info, 41762306a36Sopenharmony_ci struct page *page, u64 start, u32 len) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci struct btrfs_subpage *subpage = (struct btrfs_subpage *)page->private; 42062306a36Sopenharmony_ci unsigned int start_bit = subpage_calc_start_bit(fs_info, page, 42162306a36Sopenharmony_ci dirty, start, len); 42262306a36Sopenharmony_ci unsigned long flags; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci spin_lock_irqsave(&subpage->lock, flags); 42562306a36Sopenharmony_ci bitmap_set(subpage->bitmaps, start_bit, len >> fs_info->sectorsize_bits); 42662306a36Sopenharmony_ci spin_unlock_irqrestore(&subpage->lock, flags); 42762306a36Sopenharmony_ci set_page_dirty(page); 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci/* 43162306a36Sopenharmony_ci * Extra clear_and_test function for subpage dirty bitmap. 43262306a36Sopenharmony_ci * 43362306a36Sopenharmony_ci * Return true if we're the last bits in the dirty_bitmap and clear the 43462306a36Sopenharmony_ci * dirty_bitmap. 43562306a36Sopenharmony_ci * Return false otherwise. 43662306a36Sopenharmony_ci * 43762306a36Sopenharmony_ci * NOTE: Callers should manually clear page dirty for true case, as we have 43862306a36Sopenharmony_ci * extra handling for tree blocks. 43962306a36Sopenharmony_ci */ 44062306a36Sopenharmony_cibool btrfs_subpage_clear_and_test_dirty(const struct btrfs_fs_info *fs_info, 44162306a36Sopenharmony_ci struct page *page, u64 start, u32 len) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci struct btrfs_subpage *subpage = (struct btrfs_subpage *)page->private; 44462306a36Sopenharmony_ci unsigned int start_bit = subpage_calc_start_bit(fs_info, page, 44562306a36Sopenharmony_ci dirty, start, len); 44662306a36Sopenharmony_ci unsigned long flags; 44762306a36Sopenharmony_ci bool last = false; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci spin_lock_irqsave(&subpage->lock, flags); 45062306a36Sopenharmony_ci bitmap_clear(subpage->bitmaps, start_bit, len >> fs_info->sectorsize_bits); 45162306a36Sopenharmony_ci if (subpage_test_bitmap_all_zero(fs_info, subpage, dirty)) 45262306a36Sopenharmony_ci last = true; 45362306a36Sopenharmony_ci spin_unlock_irqrestore(&subpage->lock, flags); 45462306a36Sopenharmony_ci return last; 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_civoid btrfs_subpage_clear_dirty(const struct btrfs_fs_info *fs_info, 45862306a36Sopenharmony_ci struct page *page, u64 start, u32 len) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci bool last; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci last = btrfs_subpage_clear_and_test_dirty(fs_info, page, start, len); 46362306a36Sopenharmony_ci if (last) 46462306a36Sopenharmony_ci clear_page_dirty_for_io(page); 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_civoid btrfs_subpage_set_writeback(const struct btrfs_fs_info *fs_info, 46862306a36Sopenharmony_ci struct page *page, u64 start, u32 len) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci struct btrfs_subpage *subpage = (struct btrfs_subpage *)page->private; 47162306a36Sopenharmony_ci unsigned int start_bit = subpage_calc_start_bit(fs_info, page, 47262306a36Sopenharmony_ci writeback, start, len); 47362306a36Sopenharmony_ci unsigned long flags; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci spin_lock_irqsave(&subpage->lock, flags); 47662306a36Sopenharmony_ci bitmap_set(subpage->bitmaps, start_bit, len >> fs_info->sectorsize_bits); 47762306a36Sopenharmony_ci set_page_writeback(page); 47862306a36Sopenharmony_ci spin_unlock_irqrestore(&subpage->lock, flags); 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_civoid btrfs_subpage_clear_writeback(const struct btrfs_fs_info *fs_info, 48262306a36Sopenharmony_ci struct page *page, u64 start, u32 len) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci struct btrfs_subpage *subpage = (struct btrfs_subpage *)page->private; 48562306a36Sopenharmony_ci unsigned int start_bit = subpage_calc_start_bit(fs_info, page, 48662306a36Sopenharmony_ci writeback, start, len); 48762306a36Sopenharmony_ci unsigned long flags; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci spin_lock_irqsave(&subpage->lock, flags); 49062306a36Sopenharmony_ci bitmap_clear(subpage->bitmaps, start_bit, len >> fs_info->sectorsize_bits); 49162306a36Sopenharmony_ci if (subpage_test_bitmap_all_zero(fs_info, subpage, writeback)) { 49262306a36Sopenharmony_ci ASSERT(PageWriteback(page)); 49362306a36Sopenharmony_ci end_page_writeback(page); 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci spin_unlock_irqrestore(&subpage->lock, flags); 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_civoid btrfs_subpage_set_ordered(const struct btrfs_fs_info *fs_info, 49962306a36Sopenharmony_ci struct page *page, u64 start, u32 len) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci struct btrfs_subpage *subpage = (struct btrfs_subpage *)page->private; 50262306a36Sopenharmony_ci unsigned int start_bit = subpage_calc_start_bit(fs_info, page, 50362306a36Sopenharmony_ci ordered, start, len); 50462306a36Sopenharmony_ci unsigned long flags; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci spin_lock_irqsave(&subpage->lock, flags); 50762306a36Sopenharmony_ci bitmap_set(subpage->bitmaps, start_bit, len >> fs_info->sectorsize_bits); 50862306a36Sopenharmony_ci SetPageOrdered(page); 50962306a36Sopenharmony_ci spin_unlock_irqrestore(&subpage->lock, flags); 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_civoid btrfs_subpage_clear_ordered(const struct btrfs_fs_info *fs_info, 51362306a36Sopenharmony_ci struct page *page, u64 start, u32 len) 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci struct btrfs_subpage *subpage = (struct btrfs_subpage *)page->private; 51662306a36Sopenharmony_ci unsigned int start_bit = subpage_calc_start_bit(fs_info, page, 51762306a36Sopenharmony_ci ordered, start, len); 51862306a36Sopenharmony_ci unsigned long flags; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci spin_lock_irqsave(&subpage->lock, flags); 52162306a36Sopenharmony_ci bitmap_clear(subpage->bitmaps, start_bit, len >> fs_info->sectorsize_bits); 52262306a36Sopenharmony_ci if (subpage_test_bitmap_all_zero(fs_info, subpage, ordered)) 52362306a36Sopenharmony_ci ClearPageOrdered(page); 52462306a36Sopenharmony_ci spin_unlock_irqrestore(&subpage->lock, flags); 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_civoid btrfs_subpage_set_checked(const struct btrfs_fs_info *fs_info, 52862306a36Sopenharmony_ci struct page *page, u64 start, u32 len) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci struct btrfs_subpage *subpage = (struct btrfs_subpage *)page->private; 53162306a36Sopenharmony_ci unsigned int start_bit = subpage_calc_start_bit(fs_info, page, 53262306a36Sopenharmony_ci checked, start, len); 53362306a36Sopenharmony_ci unsigned long flags; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci spin_lock_irqsave(&subpage->lock, flags); 53662306a36Sopenharmony_ci bitmap_set(subpage->bitmaps, start_bit, len >> fs_info->sectorsize_bits); 53762306a36Sopenharmony_ci if (subpage_test_bitmap_all_set(fs_info, subpage, checked)) 53862306a36Sopenharmony_ci SetPageChecked(page); 53962306a36Sopenharmony_ci spin_unlock_irqrestore(&subpage->lock, flags); 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_civoid btrfs_subpage_clear_checked(const struct btrfs_fs_info *fs_info, 54362306a36Sopenharmony_ci struct page *page, u64 start, u32 len) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci struct btrfs_subpage *subpage = (struct btrfs_subpage *)page->private; 54662306a36Sopenharmony_ci unsigned int start_bit = subpage_calc_start_bit(fs_info, page, 54762306a36Sopenharmony_ci checked, start, len); 54862306a36Sopenharmony_ci unsigned long flags; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci spin_lock_irqsave(&subpage->lock, flags); 55162306a36Sopenharmony_ci bitmap_clear(subpage->bitmaps, start_bit, len >> fs_info->sectorsize_bits); 55262306a36Sopenharmony_ci ClearPageChecked(page); 55362306a36Sopenharmony_ci spin_unlock_irqrestore(&subpage->lock, flags); 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci/* 55762306a36Sopenharmony_ci * Unlike set/clear which is dependent on each page status, for test all bits 55862306a36Sopenharmony_ci * are tested in the same way. 55962306a36Sopenharmony_ci */ 56062306a36Sopenharmony_ci#define IMPLEMENT_BTRFS_SUBPAGE_TEST_OP(name) \ 56162306a36Sopenharmony_cibool btrfs_subpage_test_##name(const struct btrfs_fs_info *fs_info, \ 56262306a36Sopenharmony_ci struct page *page, u64 start, u32 len) \ 56362306a36Sopenharmony_ci{ \ 56462306a36Sopenharmony_ci struct btrfs_subpage *subpage = (struct btrfs_subpage *)page->private; \ 56562306a36Sopenharmony_ci unsigned int start_bit = subpage_calc_start_bit(fs_info, page, \ 56662306a36Sopenharmony_ci name, start, len); \ 56762306a36Sopenharmony_ci unsigned long flags; \ 56862306a36Sopenharmony_ci bool ret; \ 56962306a36Sopenharmony_ci \ 57062306a36Sopenharmony_ci spin_lock_irqsave(&subpage->lock, flags); \ 57162306a36Sopenharmony_ci ret = bitmap_test_range_all_set(subpage->bitmaps, start_bit, \ 57262306a36Sopenharmony_ci len >> fs_info->sectorsize_bits); \ 57362306a36Sopenharmony_ci spin_unlock_irqrestore(&subpage->lock, flags); \ 57462306a36Sopenharmony_ci return ret; \ 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ciIMPLEMENT_BTRFS_SUBPAGE_TEST_OP(uptodate); 57762306a36Sopenharmony_ciIMPLEMENT_BTRFS_SUBPAGE_TEST_OP(dirty); 57862306a36Sopenharmony_ciIMPLEMENT_BTRFS_SUBPAGE_TEST_OP(writeback); 57962306a36Sopenharmony_ciIMPLEMENT_BTRFS_SUBPAGE_TEST_OP(ordered); 58062306a36Sopenharmony_ciIMPLEMENT_BTRFS_SUBPAGE_TEST_OP(checked); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci/* 58362306a36Sopenharmony_ci * Note that, in selftests (extent-io-tests), we can have empty fs_info passed 58462306a36Sopenharmony_ci * in. We only test sectorsize == PAGE_SIZE cases so far, thus we can fall 58562306a36Sopenharmony_ci * back to regular sectorsize branch. 58662306a36Sopenharmony_ci */ 58762306a36Sopenharmony_ci#define IMPLEMENT_BTRFS_PAGE_OPS(name, set_page_func, clear_page_func, \ 58862306a36Sopenharmony_ci test_page_func) \ 58962306a36Sopenharmony_civoid btrfs_page_set_##name(const struct btrfs_fs_info *fs_info, \ 59062306a36Sopenharmony_ci struct page *page, u64 start, u32 len) \ 59162306a36Sopenharmony_ci{ \ 59262306a36Sopenharmony_ci if (unlikely(!fs_info) || !btrfs_is_subpage(fs_info, page)) { \ 59362306a36Sopenharmony_ci set_page_func(page); \ 59462306a36Sopenharmony_ci return; \ 59562306a36Sopenharmony_ci } \ 59662306a36Sopenharmony_ci btrfs_subpage_set_##name(fs_info, page, start, len); \ 59762306a36Sopenharmony_ci} \ 59862306a36Sopenharmony_civoid btrfs_page_clear_##name(const struct btrfs_fs_info *fs_info, \ 59962306a36Sopenharmony_ci struct page *page, u64 start, u32 len) \ 60062306a36Sopenharmony_ci{ \ 60162306a36Sopenharmony_ci if (unlikely(!fs_info) || !btrfs_is_subpage(fs_info, page)) { \ 60262306a36Sopenharmony_ci clear_page_func(page); \ 60362306a36Sopenharmony_ci return; \ 60462306a36Sopenharmony_ci } \ 60562306a36Sopenharmony_ci btrfs_subpage_clear_##name(fs_info, page, start, len); \ 60662306a36Sopenharmony_ci} \ 60762306a36Sopenharmony_cibool btrfs_page_test_##name(const struct btrfs_fs_info *fs_info, \ 60862306a36Sopenharmony_ci struct page *page, u64 start, u32 len) \ 60962306a36Sopenharmony_ci{ \ 61062306a36Sopenharmony_ci if (unlikely(!fs_info) || !btrfs_is_subpage(fs_info, page)) \ 61162306a36Sopenharmony_ci return test_page_func(page); \ 61262306a36Sopenharmony_ci return btrfs_subpage_test_##name(fs_info, page, start, len); \ 61362306a36Sopenharmony_ci} \ 61462306a36Sopenharmony_civoid btrfs_page_clamp_set_##name(const struct btrfs_fs_info *fs_info, \ 61562306a36Sopenharmony_ci struct page *page, u64 start, u32 len) \ 61662306a36Sopenharmony_ci{ \ 61762306a36Sopenharmony_ci if (unlikely(!fs_info) || !btrfs_is_subpage(fs_info, page)) { \ 61862306a36Sopenharmony_ci set_page_func(page); \ 61962306a36Sopenharmony_ci return; \ 62062306a36Sopenharmony_ci } \ 62162306a36Sopenharmony_ci btrfs_subpage_clamp_range(page, &start, &len); \ 62262306a36Sopenharmony_ci btrfs_subpage_set_##name(fs_info, page, start, len); \ 62362306a36Sopenharmony_ci} \ 62462306a36Sopenharmony_civoid btrfs_page_clamp_clear_##name(const struct btrfs_fs_info *fs_info, \ 62562306a36Sopenharmony_ci struct page *page, u64 start, u32 len) \ 62662306a36Sopenharmony_ci{ \ 62762306a36Sopenharmony_ci if (unlikely(!fs_info) || !btrfs_is_subpage(fs_info, page)) { \ 62862306a36Sopenharmony_ci clear_page_func(page); \ 62962306a36Sopenharmony_ci return; \ 63062306a36Sopenharmony_ci } \ 63162306a36Sopenharmony_ci btrfs_subpage_clamp_range(page, &start, &len); \ 63262306a36Sopenharmony_ci btrfs_subpage_clear_##name(fs_info, page, start, len); \ 63362306a36Sopenharmony_ci} \ 63462306a36Sopenharmony_cibool btrfs_page_clamp_test_##name(const struct btrfs_fs_info *fs_info, \ 63562306a36Sopenharmony_ci struct page *page, u64 start, u32 len) \ 63662306a36Sopenharmony_ci{ \ 63762306a36Sopenharmony_ci if (unlikely(!fs_info) || !btrfs_is_subpage(fs_info, page)) \ 63862306a36Sopenharmony_ci return test_page_func(page); \ 63962306a36Sopenharmony_ci btrfs_subpage_clamp_range(page, &start, &len); \ 64062306a36Sopenharmony_ci return btrfs_subpage_test_##name(fs_info, page, start, len); \ 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ciIMPLEMENT_BTRFS_PAGE_OPS(uptodate, SetPageUptodate, ClearPageUptodate, 64362306a36Sopenharmony_ci PageUptodate); 64462306a36Sopenharmony_ciIMPLEMENT_BTRFS_PAGE_OPS(dirty, set_page_dirty, clear_page_dirty_for_io, 64562306a36Sopenharmony_ci PageDirty); 64662306a36Sopenharmony_ciIMPLEMENT_BTRFS_PAGE_OPS(writeback, set_page_writeback, end_page_writeback, 64762306a36Sopenharmony_ci PageWriteback); 64862306a36Sopenharmony_ciIMPLEMENT_BTRFS_PAGE_OPS(ordered, SetPageOrdered, ClearPageOrdered, 64962306a36Sopenharmony_ci PageOrdered); 65062306a36Sopenharmony_ciIMPLEMENT_BTRFS_PAGE_OPS(checked, SetPageChecked, ClearPageChecked, PageChecked); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci/* 65362306a36Sopenharmony_ci * Make sure not only the page dirty bit is cleared, but also subpage dirty bit 65462306a36Sopenharmony_ci * is cleared. 65562306a36Sopenharmony_ci */ 65662306a36Sopenharmony_civoid btrfs_page_assert_not_dirty(const struct btrfs_fs_info *fs_info, 65762306a36Sopenharmony_ci struct page *page) 65862306a36Sopenharmony_ci{ 65962306a36Sopenharmony_ci struct btrfs_subpage *subpage = (struct btrfs_subpage *)page->private; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_BTRFS_ASSERT)) 66262306a36Sopenharmony_ci return; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci ASSERT(!PageDirty(page)); 66562306a36Sopenharmony_ci if (!btrfs_is_subpage(fs_info, page)) 66662306a36Sopenharmony_ci return; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci ASSERT(PagePrivate(page) && page->private); 66962306a36Sopenharmony_ci ASSERT(subpage_test_bitmap_all_zero(fs_info, subpage, dirty)); 67062306a36Sopenharmony_ci} 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci/* 67362306a36Sopenharmony_ci * Handle different locked pages with different page sizes: 67462306a36Sopenharmony_ci * 67562306a36Sopenharmony_ci * - Page locked by plain lock_page() 67662306a36Sopenharmony_ci * It should not have any subpage::writers count. 67762306a36Sopenharmony_ci * Can be unlocked by unlock_page(). 67862306a36Sopenharmony_ci * This is the most common locked page for __extent_writepage() called 67962306a36Sopenharmony_ci * inside extent_write_cache_pages(). 68062306a36Sopenharmony_ci * Rarer cases include the @locked_page from extent_write_locked_range(). 68162306a36Sopenharmony_ci * 68262306a36Sopenharmony_ci * - Page locked by lock_delalloc_pages() 68362306a36Sopenharmony_ci * There is only one caller, all pages except @locked_page for 68462306a36Sopenharmony_ci * extent_write_locked_range(). 68562306a36Sopenharmony_ci * In this case, we have to call subpage helper to handle the case. 68662306a36Sopenharmony_ci */ 68762306a36Sopenharmony_civoid btrfs_page_unlock_writer(struct btrfs_fs_info *fs_info, struct page *page, 68862306a36Sopenharmony_ci u64 start, u32 len) 68962306a36Sopenharmony_ci{ 69062306a36Sopenharmony_ci struct btrfs_subpage *subpage; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci ASSERT(PageLocked(page)); 69362306a36Sopenharmony_ci /* For non-subpage case, we just unlock the page */ 69462306a36Sopenharmony_ci if (!btrfs_is_subpage(fs_info, page)) 69562306a36Sopenharmony_ci return unlock_page(page); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci ASSERT(PagePrivate(page) && page->private); 69862306a36Sopenharmony_ci subpage = (struct btrfs_subpage *)page->private; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci /* 70162306a36Sopenharmony_ci * For subpage case, there are two types of locked page. With or 70262306a36Sopenharmony_ci * without writers number. 70362306a36Sopenharmony_ci * 70462306a36Sopenharmony_ci * Since we own the page lock, no one else could touch subpage::writers 70562306a36Sopenharmony_ci * and we are safe to do several atomic operations without spinlock. 70662306a36Sopenharmony_ci */ 70762306a36Sopenharmony_ci if (atomic_read(&subpage->writers) == 0) 70862306a36Sopenharmony_ci /* No writers, locked by plain lock_page() */ 70962306a36Sopenharmony_ci return unlock_page(page); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci /* Have writers, use proper subpage helper to end it */ 71262306a36Sopenharmony_ci btrfs_page_end_writer_lock(fs_info, page, start, len); 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci#define GET_SUBPAGE_BITMAP(subpage, subpage_info, name, dst) \ 71662306a36Sopenharmony_ci bitmap_cut(dst, subpage->bitmaps, 0, \ 71762306a36Sopenharmony_ci subpage_info->name##_offset, subpage_info->bitmap_nr_bits) 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_civoid __cold btrfs_subpage_dump_bitmap(const struct btrfs_fs_info *fs_info, 72062306a36Sopenharmony_ci struct page *page, u64 start, u32 len) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci struct btrfs_subpage_info *subpage_info = fs_info->subpage_info; 72362306a36Sopenharmony_ci struct btrfs_subpage *subpage; 72462306a36Sopenharmony_ci unsigned long uptodate_bitmap; 72562306a36Sopenharmony_ci unsigned long error_bitmap; 72662306a36Sopenharmony_ci unsigned long dirty_bitmap; 72762306a36Sopenharmony_ci unsigned long writeback_bitmap; 72862306a36Sopenharmony_ci unsigned long ordered_bitmap; 72962306a36Sopenharmony_ci unsigned long checked_bitmap; 73062306a36Sopenharmony_ci unsigned long flags; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci ASSERT(PagePrivate(page) && page->private); 73362306a36Sopenharmony_ci ASSERT(subpage_info); 73462306a36Sopenharmony_ci subpage = (struct btrfs_subpage *)page->private; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci spin_lock_irqsave(&subpage->lock, flags); 73762306a36Sopenharmony_ci GET_SUBPAGE_BITMAP(subpage, subpage_info, uptodate, &uptodate_bitmap); 73862306a36Sopenharmony_ci GET_SUBPAGE_BITMAP(subpage, subpage_info, dirty, &dirty_bitmap); 73962306a36Sopenharmony_ci GET_SUBPAGE_BITMAP(subpage, subpage_info, writeback, &writeback_bitmap); 74062306a36Sopenharmony_ci GET_SUBPAGE_BITMAP(subpage, subpage_info, ordered, &ordered_bitmap); 74162306a36Sopenharmony_ci GET_SUBPAGE_BITMAP(subpage, subpage_info, checked, &checked_bitmap); 74262306a36Sopenharmony_ci spin_unlock_irqrestore(&subpage->lock, flags); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci dump_page(page, "btrfs subpage dump"); 74562306a36Sopenharmony_ci btrfs_warn(fs_info, 74662306a36Sopenharmony_ci"start=%llu len=%u page=%llu, bitmaps uptodate=%*pbl error=%*pbl dirty=%*pbl writeback=%*pbl ordered=%*pbl checked=%*pbl", 74762306a36Sopenharmony_ci start, len, page_offset(page), 74862306a36Sopenharmony_ci subpage_info->bitmap_nr_bits, &uptodate_bitmap, 74962306a36Sopenharmony_ci subpage_info->bitmap_nr_bits, &error_bitmap, 75062306a36Sopenharmony_ci subpage_info->bitmap_nr_bits, &dirty_bitmap, 75162306a36Sopenharmony_ci subpage_info->bitmap_nr_bits, &writeback_bitmap, 75262306a36Sopenharmony_ci subpage_info->bitmap_nr_bits, &ordered_bitmap, 75362306a36Sopenharmony_ci subpage_info->bitmap_nr_bits, &checked_bitmap); 75462306a36Sopenharmony_ci} 755