[PATCH 3/5] btrfs: walk waited ordered extents in place
Johannes Thumshirn <[email protected]>
| Newsgroups | org.kernel.vger.linux-btrfs |
|---|---|
| Message-ID | <[email protected]> |
btrfs_wait_ordered_extents() splices root->ordered_extents onto a private list while it flushes and waits, making the ordered extents temporarily invisible on the root. Walk the list in place instead, moving each entry to the tail as it is processed and guarding against re-queueing with the new BTRFS_ORDERED_FLUSHING flag. No functional change for current callers; this lets a future lock-free waiter observe all in-flight ordered extents. Signed-off-by: Johannes Thumshirn <[email protected]> --- fs/btrfs/ordered-data.c | 35 +++++++++++++++++++++-------------- fs/btrfs/ordered-data.h | 2 ++ 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index b32d4eabe0ab..11aa7e8efe04 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -751,6 +751,13 @@ static void btrfs_run_ordered_extent_work(struct btrfs_work *work) complete(&ordered->completion); } +static bool ordered_in_range(const struct btrfs_ordered_extent *ordered, + u64 range_start, u64 range_end) +{ + return !(range_end <= ordered->disk_bytenr || + ordered->disk_bytenr + ordered->disk_num_bytes <= range_start); +} + /* * Wait for all the ordered extents in a root. Use @bg as range or do whole * range if it's NULL. @@ -759,13 +766,12 @@ u64 btrfs_wait_ordered_extents(struct btrfs_root *root, u64 nr, const struct btrfs_block_group *bg) { struct btrfs_fs_info *fs_info = root->fs_info; - LIST_HEAD(splice); - LIST_HEAD(skipped); LIST_HEAD(works); struct btrfs_ordered_extent *ordered, *next; u64 count = 0; u64 range_start, range_len; u64 range_end; + u64 remaining; if (bg) { range_start = bg->start; @@ -778,20 +784,22 @@ u64 btrfs_wait_ordered_extents(struct btrfs_root *root, u64 nr, mutex_lock(&root->ordered_extent_mutex); spin_lock(&root->ordered_extent_lock); - list_splice_init(&root->ordered_extents, &splice); - while (!list_empty(&splice) && nr) { - ordered = list_first_entry(&splice, struct btrfs_ordered_extent, + remaining = root->nr_ordered_extents; + while (remaining && nr && !list_empty(&root->ordered_extents)) { + remaining--; + ordered = list_first_entry(&root->ordered_extents, + struct btrfs_ordered_extent, root_extent_list); + list_move_tail(&ordered->root_extent_list, + &root->ordered_extents); - if (range_end <= ordered->disk_bytenr || - ordered->disk_bytenr + ordered->disk_num_bytes <= range_start) { - list_move_tail(&ordered->root_extent_list, &skipped); - cond_resched_lock(&root->ordered_extent_lock); + if (!ordered_in_range(ordered, range_start, range_end)) + continue; + + /* Already queued to 'works' by this call, skip on revisit. */ + if (test_and_set_bit(BTRFS_ORDERED_FLUSHING, &ordered->flags)) continue; - } - list_move_tail(&ordered->root_extent_list, - &root->ordered_extents); refcount_inc(&ordered->refs); spin_unlock(&root->ordered_extent_lock); @@ -806,13 +814,12 @@ u64 btrfs_wait_ordered_extents(struct btrfs_root *root, u64 nr, count++; spin_lock(&root->ordered_extent_lock); } - list_splice_tail(&skipped, &root->ordered_extents); - list_splice_tail(&splice, &root->ordered_extents); spin_unlock(&root->ordered_extent_lock); list_for_each_entry_safe(ordered, next, &works, work_list) { list_del_init(&ordered->work_list); wait_for_completion(&ordered->completion); + clear_bit(BTRFS_ORDERED_FLUSHING, &ordered->flags); btrfs_put_ordered_extent(ordered); cond_resched(); } diff --git a/fs/btrfs/ordered-data.h b/fs/btrfs/ordered-data.h index 8d5d5ba1e02f..f4d0675bb9c3 100644 --- a/fs/btrfs/ordered-data.h +++ b/fs/btrfs/ordered-data.h @@ -63,6 +63,8 @@ enum { BTRFS_ORDERED_LOGGED_CSUM, /* We wait for this extent to complete in the current transaction. */ BTRFS_ORDERED_PENDING, + /* Flush work is queued by btrfs_wait_ordered_extents(), avoid requeue. */ + BTRFS_ORDERED_FLUSHING, /* * Different types for ordered extents, one and only one of these types -- 2.54.0