Skip to content

Commit d7172f5

Browse files
Christoph Hellwigkdave
authored andcommitted
btrfs: use per-buffer locking for extent_buffer reading
Instead of locking and unlocking every page or the extent, just add a new EXTENT_BUFFER_READING bit that mirrors EXTENT_BUFFER_WRITEBACK for synchronizing threads trying to read an extent_buffer and to wait for I/O completion. Reviewed-by: Josef Bacik <josef@toxicpanda.com> Signed-off-by: Christoph Hellwig <hch@lst.de> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
1 parent 9e2aff9 commit d7172f5

File tree

2 files changed

+37
-115
lines changed

2 files changed

+37
-115
lines changed

fs/btrfs/extent_io.c

Lines changed: 35 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -4049,6 +4049,7 @@ void set_extent_buffer_uptodate(struct extent_buffer *eb)
40494049
static void extent_buffer_read_end_io(struct btrfs_bio *bbio)
40504050
{
40514051
struct extent_buffer *eb = bbio->private;
4052+
struct btrfs_fs_info *fs_info = eb->fs_info;
40524053
bool uptodate = !bbio->bio.bi_status;
40534054
struct bvec_iter_all iter_all;
40544055
struct bio_vec *bvec;
@@ -4068,26 +4069,47 @@ static void extent_buffer_read_end_io(struct btrfs_bio *bbio)
40684069
}
40694070

40704071
bio_for_each_segment_all(bvec, &bbio->bio, iter_all) {
4071-
end_page_read(bvec->bv_page, uptodate, eb->start + bio_offset,
4072-
bvec->bv_len);
4073-
bio_offset += bvec->bv_len;
4074-
}
4072+
u64 start = eb->start + bio_offset;
4073+
struct page *page = bvec->bv_page;
4074+
u32 len = bvec->bv_len;
40754075

4076-
if (eb->fs_info->nodesize < PAGE_SIZE) {
4077-
unlock_extent(&bbio->inode->io_tree, eb->start,
4078-
eb->start + bio_offset - 1, NULL);
4076+
if (uptodate)
4077+
btrfs_page_set_uptodate(fs_info, page, start, len);
4078+
else
4079+
btrfs_page_clear_uptodate(fs_info, page, start, len);
4080+
4081+
bio_offset += len;
40794082
}
4083+
4084+
clear_bit(EXTENT_BUFFER_READING, &eb->bflags);
4085+
smp_mb__after_atomic();
4086+
wake_up_bit(&eb->bflags, EXTENT_BUFFER_READING);
40804087
free_extent_buffer(eb);
40814088

40824089
bio_put(&bbio->bio);
40834090
}
40844091

4085-
static void __read_extent_buffer_pages(struct extent_buffer *eb, int mirror_num,
4086-
struct btrfs_tree_parent_check *check)
4092+
int read_extent_buffer_pages(struct extent_buffer *eb, int wait, int mirror_num,
4093+
struct btrfs_tree_parent_check *check)
40874094
{
40884095
int num_pages = num_extent_pages(eb), i;
40894096
struct btrfs_bio *bbio;
40904097

4098+
if (test_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags))
4099+
return 0;
4100+
4101+
/*
4102+
* We could have had EXTENT_BUFFER_UPTODATE cleared by the write
4103+
* operation, which could potentially still be in flight. In this case
4104+
* we simply want to return an error.
4105+
*/
4106+
if (unlikely(test_bit(EXTENT_BUFFER_WRITE_ERR, &eb->bflags)))
4107+
return -EIO;
4108+
4109+
/* Someone else is already reading the buffer, just wait for it. */
4110+
if (test_and_set_bit(EXTENT_BUFFER_READING, &eb->bflags))
4111+
goto done;
4112+
40914113
clear_bit(EXTENT_BUFFER_READ_ERR, &eb->bflags);
40924114
eb->read_mirror = 0;
40934115
check_buffer_tree_ref(eb);
@@ -4108,117 +4130,15 @@ static void __read_extent_buffer_pages(struct extent_buffer *eb, int mirror_num,
41084130
__bio_add_page(&bbio->bio, eb->pages[i], PAGE_SIZE, 0);
41094131
}
41104132
btrfs_submit_bio(bbio, mirror_num);
4111-
}
4112-
4113-
static int read_extent_buffer_subpage(struct extent_buffer *eb, int wait,
4114-
int mirror_num,
4115-
struct btrfs_tree_parent_check *check)
4116-
{
4117-
struct btrfs_fs_info *fs_info = eb->fs_info;
4118-
struct extent_io_tree *io_tree;
4119-
struct page *page = eb->pages[0];
4120-
struct extent_state *cached_state = NULL;
4121-
int ret;
4122-
4123-
ASSERT(!test_bit(EXTENT_BUFFER_UNMAPPED, &eb->bflags));
4124-
ASSERT(PagePrivate(page));
4125-
ASSERT(check);
4126-
io_tree = &BTRFS_I(fs_info->btree_inode)->io_tree;
4127-
4128-
if (wait == WAIT_NONE) {
4129-
if (!try_lock_extent(io_tree, eb->start, eb->start + eb->len - 1,
4130-
&cached_state))
4131-
return -EAGAIN;
4132-
} else {
4133-
ret = lock_extent(io_tree, eb->start, eb->start + eb->len - 1,
4134-
&cached_state);
4135-
if (ret < 0)
4136-
return ret;
4137-
}
4138-
4139-
if (test_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags)) {
4140-
unlock_extent(io_tree, eb->start, eb->start + eb->len - 1,
4141-
&cached_state);
4142-
return 0;
4143-
}
4144-
4145-
btrfs_subpage_start_reader(fs_info, page, eb->start, eb->len);
41464133

4147-
__read_extent_buffer_pages(eb, mirror_num, check);
4148-
if (wait != WAIT_COMPLETE) {
4149-
free_extent_state(cached_state);
4150-
return 0;
4151-
}
4152-
4153-
wait_extent_bit(io_tree, eb->start, eb->start + eb->len - 1,
4154-
EXTENT_LOCKED, &cached_state);
4155-
if (!test_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags))
4156-
return -EIO;
4157-
return 0;
4158-
}
4159-
4160-
int read_extent_buffer_pages(struct extent_buffer *eb, int wait, int mirror_num,
4161-
struct btrfs_tree_parent_check *check)
4162-
{
4163-
int i;
4164-
struct page *page;
4165-
int locked_pages = 0;
4166-
int num_pages;
4167-
4168-
if (test_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags))
4169-
return 0;
4170-
4171-
/*
4172-
* We could have had EXTENT_BUFFER_UPTODATE cleared by the write
4173-
* operation, which could potentially still be in flight. In this case
4174-
* we simply want to return an error.
4175-
*/
4176-
if (unlikely(test_bit(EXTENT_BUFFER_WRITE_ERR, &eb->bflags)))
4177-
return -EIO;
4178-
4179-
if (eb->fs_info->nodesize < PAGE_SIZE)
4180-
return read_extent_buffer_subpage(eb, wait, mirror_num, check);
4181-
4182-
num_pages = num_extent_pages(eb);
4183-
for (i = 0; i < num_pages; i++) {
4184-
page = eb->pages[i];
4185-
if (wait == WAIT_NONE) {
4186-
/*
4187-
* WAIT_NONE is only utilized by readahead. If we can't
4188-
* acquire the lock atomically it means either the eb
4189-
* is being read out or under modification.
4190-
* Either way the eb will be or has been cached,
4191-
* readahead can exit safely.
4192-
*/
4193-
if (!trylock_page(page))
4194-
goto unlock_exit;
4195-
} else {
4196-
lock_page(page);
4197-
}
4198-
locked_pages++;
4199-
}
4200-
4201-
__read_extent_buffer_pages(eb, mirror_num, check);
4202-
4203-
if (wait != WAIT_COMPLETE)
4204-
return 0;
4205-
4206-
for (i = 0; i < num_pages; i++) {
4207-
page = eb->pages[i];
4208-
wait_on_page_locked(page);
4209-
if (!PageUptodate(page))
4134+
done:
4135+
if (wait == WAIT_COMPLETE) {
4136+
wait_on_bit_io(&eb->bflags, EXTENT_BUFFER_READING, TASK_UNINTERRUPTIBLE);
4137+
if (!test_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags))
42104138
return -EIO;
42114139
}
42124140

42134141
return 0;
4214-
4215-
unlock_exit:
4216-
while (locked_pages > 0) {
4217-
locked_pages--;
4218-
page = eb->pages[locked_pages];
4219-
unlock_page(page);
4220-
}
4221-
return 0;
42224142
}
42234143

42244144
static bool report_eb_range(const struct extent_buffer *eb, unsigned long start,

fs/btrfs/extent_io.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ enum {
2929
/* write IO error */
3030
EXTENT_BUFFER_WRITE_ERR,
3131
EXTENT_BUFFER_NO_CHECK,
32+
/* Indicate that extent buffer pages a being read */
33+
EXTENT_BUFFER_READING,
3234
};
3335

3436
/* these are flags for __process_pages_contig */

0 commit comments

Comments
 (0)