Message ID | 996c0c3b0807f46f7ae722541e6a90c87b7d3e58.1716008374.git.wqu@suse.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | btrfs: subpage + zoned fixes | expand |
On Sat, May 18, 2024 at 02:37:40PM GMT, Qu Wenruo wrote: > Three new helpers are introduced for the incoming subpage delalloc locking > change. > > - btrfs_folio_set_writer_lock() > This is to mark specified range with subpage specific writer lock. > After calling this, the subpage range can be proper unlocked by > btrfs_folio_end_writer_lock() > > - btrfs_subpage_find_writer_locked() > This is to find the writer locked subpage range in a page. > With the help of btrfs_folio_set_writer_lock(), it can allow us to > record and find previously locked subpage range without extra memory > allocation. > > - btrfs_folio_end_all_writers() > This is for the locked_page of __extent_writepage(), as there may be > multiple subpage delalloc ranges locked. > > Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com> > Signed-off-by: Qu Wenruo <wqu@suse.com> > --- There are some nits inlined below, basically it looks good. Reviewed-by: Naohiro Aota <naohiro.aota@wdc.com> > fs/btrfs/subpage.c | 116 +++++++++++++++++++++++++++++++++++++++++++++ > fs/btrfs/subpage.h | 7 +++ > 2 files changed, 123 insertions(+) > > diff --git a/fs/btrfs/subpage.c b/fs/btrfs/subpage.c > index 183b32f51f51..3c957d03324e 100644 > --- a/fs/btrfs/subpage.c > +++ b/fs/btrfs/subpage.c > @@ -775,6 +775,122 @@ void btrfs_folio_unlock_writer(struct btrfs_fs_info *fs_info, > btrfs_folio_end_writer_lock(fs_info, folio, start, len); > } > > +/* > + * This is for folio already locked by plain lock_page()/folio_lock(), which > + * doesn't have any subpage awareness. > + * > + * This would populate the involved subpage ranges so that subpage helpers can > + * properly unlock them. > + */ > +void btrfs_folio_set_writer_lock(const struct btrfs_fs_info *fs_info, > + struct folio *folio, u64 start, u32 len) > +{ > + struct btrfs_subpage *subpage; > + unsigned long flags; > + int start_bit; > + int nbits; May want to use unsigned int for a consistency... > + int ret; > + > + ASSERT(folio_test_locked(folio)); > + if (unlikely(!fs_info) || !btrfs_is_subpage(fs_info, folio->mapping)) > + return; > + > + subpage = folio_get_private(folio); > + start_bit = subpage_calc_start_bit(fs_info, folio, locked, start, len); > + nbits = len >> fs_info->sectorsize_bits; > + spin_lock_irqsave(&subpage->lock, flags); > + /* Target range should not yet be locked. */ > + ASSERT(bitmap_test_range_all_zero(subpage->bitmaps, start_bit, nbits)); > + bitmap_set(subpage->bitmaps, start_bit, nbits); > + ret = atomic_add_return(nbits, &subpage->writers); > + ASSERT(ret <= fs_info->subpage_info->bitmap_nr_bits); > + spin_unlock_irqrestore(&subpage->lock, flags); > +} > + > +/* > + * Find any subpage writer locked range inside @folio, starting at file offset > + * @search_start. > + * The caller should ensure the folio is locked. > + * > + * Return true and update @found_start_ret and @found_len_ret to the first > + * writer locked range. > + * Return false if there is no writer locked range. > + */ > +bool btrfs_subpage_find_writer_locked(const struct btrfs_fs_info *fs_info, > + struct folio *folio, u64 search_start, > + u64 *found_start_ret, u32 *found_len_ret) > +{ > + struct btrfs_subpage_info *subpage_info = fs_info->subpage_info; > + struct btrfs_subpage *subpage = folio_get_private(folio); > + const int len = PAGE_SIZE - offset_in_page(search_start); > + const int start_bit = subpage_calc_start_bit(fs_info, folio, locked, > + search_start, len); > + const int locked_bitmap_start = subpage_info->locked_offset; > + const int locked_bitmap_end = locked_bitmap_start + > + subpage_info->bitmap_nr_bits; > + unsigned long flags; > + int first_zero; > + int first_set; > + bool found = false; > + > + ASSERT(folio_test_locked(folio)); > + spin_lock_irqsave(&subpage->lock, flags); > + first_set = find_next_bit(subpage->bitmaps, locked_bitmap_end, > + start_bit); > + if (first_set >= locked_bitmap_end) > + goto out; > + > + found = true; > + *found_start_ret = folio_pos(folio) + > + ((first_set - locked_bitmap_start) << fs_info->sectorsize_bits); It's a bit fearful to see an "int" value is shifted and added into u64 value. But, I guess sectorsize is within 32-bit range, right? > + > + first_zero = find_next_zero_bit(subpage->bitmaps, > + locked_bitmap_end, first_set); > + *found_len_ret = (first_zero - first_set) << fs_info->sectorsize_bits; > +out: > + spin_unlock_irqrestore(&subpage->lock, flags); > + return found; > +} > + > +/* > + * Unlike btrfs_folio_end_writer_lock() which unlock a specified subpage range, > + * this would end all writer locked ranges of a page. > + * > + * This is for the locked page of __extent_writepage(), as the locked page > + * can contain several locked subpage ranges. > + */ > +void btrfs_folio_end_all_writers(const struct btrfs_fs_info *fs_info, > + struct folio *folio) > +{ > + u64 folio_start = folio_pos(folio); > + u64 cur = folio_start; > + > + ASSERT(folio_test_locked(folio)); > + if (!btrfs_is_subpage(fs_info, folio->mapping)) { > + folio_unlock(folio); > + return; > + } > + > + while (cur < folio_start + PAGE_SIZE) { > + u64 found_start; > + u32 found_len; > + bool found; > + bool last; > + > + found = btrfs_subpage_find_writer_locked(fs_info, folio, cur, > + &found_start, &found_len); > + if (!found) > + break; > + last = btrfs_subpage_end_and_test_writer(fs_info, folio, > + found_start, found_len); > + if (last) { > + folio_unlock(folio); > + break; > + } > + cur = found_start + found_len; > + } > +} > + > #define GET_SUBPAGE_BITMAP(subpage, subpage_info, name, dst) \ > bitmap_cut(dst, subpage->bitmaps, 0, \ > subpage_info->name##_offset, subpage_info->bitmap_nr_bits) > diff --git a/fs/btrfs/subpage.h b/fs/btrfs/subpage.h > index 4b363d9453af..9f19850d59f2 100644 > --- a/fs/btrfs/subpage.h > +++ b/fs/btrfs/subpage.h > @@ -112,6 +112,13 @@ int btrfs_folio_start_writer_lock(const struct btrfs_fs_info *fs_info, > struct folio *folio, u64 start, u32 len); > void btrfs_folio_end_writer_lock(const struct btrfs_fs_info *fs_info, > struct folio *folio, u64 start, u32 len); > +void btrfs_folio_set_writer_lock(const struct btrfs_fs_info *fs_info, > + struct folio *folio, u64 start, u32 len); > +bool btrfs_subpage_find_writer_locked(const struct btrfs_fs_info *fs_info, > + struct folio *folio, u64 search_start, > + u64 *found_start_ret, u32 *found_len_ret); > +void btrfs_folio_end_all_writers(const struct btrfs_fs_info *fs_info, > + struct folio *folio); > > /* > * Template for subpage related operations. > -- > 2.45.0 >
在 2024/5/21 17:20, Naohiro Aota 写道: [...] >> +void btrfs_folio_set_writer_lock(const struct btrfs_fs_info *fs_info, >> + struct folio *folio, u64 start, u32 len) >> +{ >> + struct btrfs_subpage *subpage; >> + unsigned long flags; >> + int start_bit; >> + int nbits; > > May want to use unsigned int for a consistency... > I can definitely change all the int to "unsigned int" to be consistent during pushing to for-next branch. [...] >> + found = true; >> + *found_start_ret = folio_pos(folio) + >> + ((first_set - locked_bitmap_start) << fs_info->sectorsize_bits); > > It's a bit fearful to see an "int" value is shifted and added into u64 > value. But, I guess sectorsize is within 32-bit range, right? In fact, (first_set - locked_bitmap_start) is never going to be larger than (PAGE_SIZE / sectorsize). I can add extra ASSERT() to be extra safe for that too. Thanks, Qu > >> + >> + first_zero = find_next_zero_bit(subpage->bitmaps, >> + locked_bitmap_end, first_set); >> + *found_len_ret = (first_zero - first_set) << fs_info->sectorsize_bits; >> +out: >> + spin_unlock_irqrestore(&subpage->lock, flags); >> + return found; >> +} >> + >> +/* >> + * Unlike btrfs_folio_end_writer_lock() which unlock a specified subpage range, >> + * this would end all writer locked ranges of a page. >> + * >> + * This is for the locked page of __extent_writepage(), as the locked page >> + * can contain several locked subpage ranges. >> + */ >> +void btrfs_folio_end_all_writers(const struct btrfs_fs_info *fs_info, >> + struct folio *folio) >> +{ >> + u64 folio_start = folio_pos(folio); >> + u64 cur = folio_start; >> + >> + ASSERT(folio_test_locked(folio)); >> + if (!btrfs_is_subpage(fs_info, folio->mapping)) { >> + folio_unlock(folio); >> + return; >> + } >> + >> + while (cur < folio_start + PAGE_SIZE) { >> + u64 found_start; >> + u32 found_len; >> + bool found; >> + bool last; >> + >> + found = btrfs_subpage_find_writer_locked(fs_info, folio, cur, >> + &found_start, &found_len); >> + if (!found) >> + break; >> + last = btrfs_subpage_end_and_test_writer(fs_info, folio, >> + found_start, found_len); >> + if (last) { >> + folio_unlock(folio); >> + break; >> + } >> + cur = found_start + found_len; >> + } >> +} >> + >> #define GET_SUBPAGE_BITMAP(subpage, subpage_info, name, dst) \ >> bitmap_cut(dst, subpage->bitmaps, 0, \ >> subpage_info->name##_offset, subpage_info->bitmap_nr_bits) >> diff --git a/fs/btrfs/subpage.h b/fs/btrfs/subpage.h >> index 4b363d9453af..9f19850d59f2 100644 >> --- a/fs/btrfs/subpage.h >> +++ b/fs/btrfs/subpage.h >> @@ -112,6 +112,13 @@ int btrfs_folio_start_writer_lock(const struct btrfs_fs_info *fs_info, >> struct folio *folio, u64 start, u32 len); >> void btrfs_folio_end_writer_lock(const struct btrfs_fs_info *fs_info, >> struct folio *folio, u64 start, u32 len); >> +void btrfs_folio_set_writer_lock(const struct btrfs_fs_info *fs_info, >> + struct folio *folio, u64 start, u32 len); >> +bool btrfs_subpage_find_writer_locked(const struct btrfs_fs_info *fs_info, >> + struct folio *folio, u64 search_start, >> + u64 *found_start_ret, u32 *found_len_ret); >> +void btrfs_folio_end_all_writers(const struct btrfs_fs_info *fs_info, >> + struct folio *folio); >> >> /* >> * Template for subpage related operations. >> -- >> 2.45.0 >>
diff --git a/fs/btrfs/subpage.c b/fs/btrfs/subpage.c index 183b32f51f51..3c957d03324e 100644 --- a/fs/btrfs/subpage.c +++ b/fs/btrfs/subpage.c @@ -775,6 +775,122 @@ void btrfs_folio_unlock_writer(struct btrfs_fs_info *fs_info, btrfs_folio_end_writer_lock(fs_info, folio, start, len); } +/* + * This is for folio already locked by plain lock_page()/folio_lock(), which + * doesn't have any subpage awareness. + * + * This would populate the involved subpage ranges so that subpage helpers can + * properly unlock them. + */ +void btrfs_folio_set_writer_lock(const struct btrfs_fs_info *fs_info, + struct folio *folio, u64 start, u32 len) +{ + struct btrfs_subpage *subpage; + unsigned long flags; + int start_bit; + int nbits; + int ret; + + ASSERT(folio_test_locked(folio)); + if (unlikely(!fs_info) || !btrfs_is_subpage(fs_info, folio->mapping)) + return; + + subpage = folio_get_private(folio); + start_bit = subpage_calc_start_bit(fs_info, folio, locked, start, len); + nbits = len >> fs_info->sectorsize_bits; + spin_lock_irqsave(&subpage->lock, flags); + /* Target range should not yet be locked. */ + ASSERT(bitmap_test_range_all_zero(subpage->bitmaps, start_bit, nbits)); + bitmap_set(subpage->bitmaps, start_bit, nbits); + ret = atomic_add_return(nbits, &subpage->writers); + ASSERT(ret <= fs_info->subpage_info->bitmap_nr_bits); + spin_unlock_irqrestore(&subpage->lock, flags); +} + +/* + * Find any subpage writer locked range inside @folio, starting at file offset + * @search_start. + * The caller should ensure the folio is locked. + * + * Return true and update @found_start_ret and @found_len_ret to the first + * writer locked range. + * Return false if there is no writer locked range. + */ +bool btrfs_subpage_find_writer_locked(const struct btrfs_fs_info *fs_info, + struct folio *folio, u64 search_start, + u64 *found_start_ret, u32 *found_len_ret) +{ + struct btrfs_subpage_info *subpage_info = fs_info->subpage_info; + struct btrfs_subpage *subpage = folio_get_private(folio); + const int len = PAGE_SIZE - offset_in_page(search_start); + const int start_bit = subpage_calc_start_bit(fs_info, folio, locked, + search_start, len); + const int locked_bitmap_start = subpage_info->locked_offset; + const int locked_bitmap_end = locked_bitmap_start + + subpage_info->bitmap_nr_bits; + unsigned long flags; + int first_zero; + int first_set; + bool found = false; + + ASSERT(folio_test_locked(folio)); + spin_lock_irqsave(&subpage->lock, flags); + first_set = find_next_bit(subpage->bitmaps, locked_bitmap_end, + start_bit); + if (first_set >= locked_bitmap_end) + goto out; + + found = true; + *found_start_ret = folio_pos(folio) + + ((first_set - locked_bitmap_start) << fs_info->sectorsize_bits); + + first_zero = find_next_zero_bit(subpage->bitmaps, + locked_bitmap_end, first_set); + *found_len_ret = (first_zero - first_set) << fs_info->sectorsize_bits; +out: + spin_unlock_irqrestore(&subpage->lock, flags); + return found; +} + +/* + * Unlike btrfs_folio_end_writer_lock() which unlock a specified subpage range, + * this would end all writer locked ranges of a page. + * + * This is for the locked page of __extent_writepage(), as the locked page + * can contain several locked subpage ranges. + */ +void btrfs_folio_end_all_writers(const struct btrfs_fs_info *fs_info, + struct folio *folio) +{ + u64 folio_start = folio_pos(folio); + u64 cur = folio_start; + + ASSERT(folio_test_locked(folio)); + if (!btrfs_is_subpage(fs_info, folio->mapping)) { + folio_unlock(folio); + return; + } + + while (cur < folio_start + PAGE_SIZE) { + u64 found_start; + u32 found_len; + bool found; + bool last; + + found = btrfs_subpage_find_writer_locked(fs_info, folio, cur, + &found_start, &found_len); + if (!found) + break; + last = btrfs_subpage_end_and_test_writer(fs_info, folio, + found_start, found_len); + if (last) { + folio_unlock(folio); + break; + } + cur = found_start + found_len; + } +} + #define GET_SUBPAGE_BITMAP(subpage, subpage_info, name, dst) \ bitmap_cut(dst, subpage->bitmaps, 0, \ subpage_info->name##_offset, subpage_info->bitmap_nr_bits) diff --git a/fs/btrfs/subpage.h b/fs/btrfs/subpage.h index 4b363d9453af..9f19850d59f2 100644 --- a/fs/btrfs/subpage.h +++ b/fs/btrfs/subpage.h @@ -112,6 +112,13 @@ int btrfs_folio_start_writer_lock(const struct btrfs_fs_info *fs_info, struct folio *folio, u64 start, u32 len); void btrfs_folio_end_writer_lock(const struct btrfs_fs_info *fs_info, struct folio *folio, u64 start, u32 len); +void btrfs_folio_set_writer_lock(const struct btrfs_fs_info *fs_info, + struct folio *folio, u64 start, u32 len); +bool btrfs_subpage_find_writer_locked(const struct btrfs_fs_info *fs_info, + struct folio *folio, u64 search_start, + u64 *found_start_ret, u32 *found_len_ret); +void btrfs_folio_end_all_writers(const struct btrfs_fs_info *fs_info, + struct folio *folio); /* * Template for subpage related operations.