Skip to content

Commit 8e7e9c6

Browse files
adam900710kdave
authored andcommitted
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: Qu Wenruo <[email protected]> Reviewed-by: David Sterba <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent 621b9ff commit 8e7e9c6

File tree

2 files changed

+46
-11
lines changed

2 files changed

+46
-11
lines changed

fs/btrfs/subpage.c

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ void btrfs_init_subpage_info(struct btrfs_subpage_info *subpage_info, u32 sector
111111
subpage_info->checked_offset = cur;
112112
cur += nr_bits;
113113

114+
subpage_info->locked_offset = cur;
115+
cur += nr_bits;
116+
114117
subpage_info->total_nr_bits = cur;
115118
}
116119

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

243+
#define subpage_calc_start_bit(fs_info, folio, name, start, len) \
244+
({ \
245+
unsigned int start_bit; \
246+
\
247+
btrfs_subpage_assert(fs_info, folio, start, len); \
248+
start_bit = offset_in_page(start) >> fs_info->sectorsize_bits; \
249+
start_bit += fs_info->subpage_info->name##_offset; \
250+
start_bit; \
251+
})
252+
240253
void btrfs_subpage_start_reader(const struct btrfs_fs_info *fs_info,
241254
struct folio *folio, u64 start, u32 len)
242255
{
243256
struct btrfs_subpage *subpage = folio_get_private(folio);
257+
const int start_bit = subpage_calc_start_bit(fs_info, folio, locked, start, len);
244258
const int nbits = len >> fs_info->sectorsize_bits;
259+
unsigned long flags;
260+
245261

246262
btrfs_subpage_assert(fs_info, folio, start, len);
247263

264+
spin_lock_irqsave(&subpage->lock, flags);
265+
/*
266+
* Even though it's just for reading the page, no one should have
267+
* locked the subpage range.
268+
*/
269+
ASSERT(bitmap_test_range_all_zero(subpage->bitmaps, start_bit, nbits));
270+
bitmap_set(subpage->bitmaps, start_bit, nbits);
248271
atomic_add(nbits, &subpage->readers);
272+
spin_unlock_irqrestore(&subpage->lock, flags);
249273
}
250274

251275
void btrfs_subpage_end_reader(const struct btrfs_fs_info *fs_info,
252276
struct folio *folio, u64 start, u32 len)
253277
{
254278
struct btrfs_subpage *subpage = folio_get_private(folio);
279+
const int start_bit = subpage_calc_start_bit(fs_info, folio, locked, start, len);
255280
const int nbits = len >> fs_info->sectorsize_bits;
281+
unsigned long flags;
256282
bool is_data;
257283
bool last;
258284

259285
btrfs_subpage_assert(fs_info, folio, start, len);
260286
is_data = is_data_inode(folio->mapping->host);
287+
288+
spin_lock_irqsave(&subpage->lock, flags);
289+
290+
/* The range should have already been locked. */
291+
ASSERT(bitmap_test_range_all_set(subpage->bitmaps, start_bit, nbits));
261292
ASSERT(atomic_read(&subpage->readers) >= nbits);
293+
294+
bitmap_clear(subpage->bitmaps, start_bit, nbits);
262295
last = atomic_sub_and_test(nbits, &subpage->readers);
263296

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

275309
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,
365399
folio_unlock(folio);
366400
}
367401

368-
#define subpage_calc_start_bit(fs_info, folio, name, start, len) \
369-
({ \
370-
unsigned int start_bit; \
371-
\
372-
btrfs_subpage_assert(fs_info, folio, start, len); \
373-
start_bit = offset_in_page(start) >> fs_info->sectorsize_bits; \
374-
start_bit += fs_info->subpage_info->name##_offset; \
375-
start_bit; \
376-
})
377-
378402
#define subpage_test_bitmap_all_set(fs_info, subpage, name) \
379403
bitmap_test_range_all_set(subpage->bitmaps, \
380404
fs_info->subpage_info->name##_offset, \
@@ -751,6 +775,7 @@ void __cold btrfs_subpage_dump_bitmap(const struct btrfs_fs_info *fs_info,
751775
GET_SUBPAGE_BITMAP(subpage, subpage_info, writeback, &writeback_bitmap);
752776
GET_SUBPAGE_BITMAP(subpage, subpage_info, ordered, &ordered_bitmap);
753777
GET_SUBPAGE_BITMAP(subpage, subpage_info, checked, &checked_bitmap);
778+
GET_SUBPAGE_BITMAP(subpage, subpage_info, locked, &checked_bitmap);
754779
spin_unlock_irqrestore(&subpage->lock, flags);
755780

756781
dump_page(folio_page(folio, 0), "btrfs subpage dump");

fs/btrfs/subpage.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,24 @@ struct btrfs_subpage_info {
3333
unsigned int total_nr_bits;
3434

3535
/*
36-
* *_start indicates where the bitmap starts, the length is always
36+
* *_offset indicates where the bitmap starts, the length is always
3737
* @bitmap_size, which is calculated from PAGE_SIZE / sectorsize.
3838
*/
3939
unsigned int uptodate_offset;
4040
unsigned int dirty_offset;
4141
unsigned int writeback_offset;
4242
unsigned int ordered_offset;
4343
unsigned int checked_offset;
44+
45+
/*
46+
* For locked bitmaps, normally it's subpage representation for folio
47+
* Locked flag, but metadata is different:
48+
*
49+
* - Metadata doesn't really lock the folio
50+
* It's just to prevent page::private get cleared before the last
51+
* end_page_read().
52+
*/
53+
unsigned int locked_offset;
4454
};
4555

4656
/*

0 commit comments

Comments
 (0)