Skip to content

Commit

Permalink
Merge tag 'for-5.6-tag' of git://git.kernel.org/pub/scm/linux/kernel/…
Browse files Browse the repository at this point in the history
…git/kdave/linux

Pull more btrfs updates from David Sterba:
 "Fixes that arrived after the merge window freeze, mostly stable
  material.

   - fix race in tree-mod-log element tracking

   - fix bio flushing inside extent writepages

   - fix assertion when in-memory tracking of discarded extents finds an
     empty tree (eg. after adding a new device)

   - update logic of temporary read-only block groups to take into
     account overcommit

   - fix some fixup worker corner cases:
       - page could not go through proper COW cycle and the dirty status
         is lost due to page migration
       - deadlock if delayed allocation is performed under page lock

   - fix send emitting invalid clones within the same file

   - fix statfs reporting 0 free space when global block reserve size is
     larger than remaining free space but there is still space for new
     chunks"

* tag 'for-5.6-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux:
  btrfs: do not zero f_bavail if we have available space
  Btrfs: send, fix emission of invalid clone operations within the same file
  btrfs: do not do delalloc reservation under page lock
  btrfs: drop the -EBUSY case in __extent_writepage_io
  Btrfs: keep pages dirty when using btrfs_writepage_fixup_worker
  btrfs: take overcommit into account in inc_block_group_ro
  btrfs: fix force usage in inc_block_group_ro
  btrfs: Correctly handle empty trees in find_first_clear_extent_bit
  btrfs: flush write bio if we loop in extent_write_cache_pages
  Btrfs: fix race between adding and putting tree mod seq elements and nodes
  • Loading branch information
Linus Torvalds committed Feb 3, 2020
2 parents e17ac02 + d55966c commit ad80142
Show file tree
Hide file tree
Showing 13 changed files with 193 additions and 83 deletions.
39 changes: 27 additions & 12 deletions fs/btrfs/block-group.c
Original file line number Diff line number Diff line change
Expand Up @@ -1191,7 +1191,6 @@ static int inc_block_group_ro(struct btrfs_block_group *cache, int force)
{
struct btrfs_space_info *sinfo = cache->space_info;
u64 num_bytes;
u64 sinfo_used;
int ret = -ENOSPC;

spin_lock(&sinfo->lock);
Expand All @@ -1205,29 +1204,45 @@ static int inc_block_group_ro(struct btrfs_block_group *cache, int force)

num_bytes = cache->length - cache->reserved - cache->pinned -
cache->bytes_super - cache->used;
sinfo_used = btrfs_space_info_used(sinfo, true);

/*
* sinfo_used + num_bytes should always <= sinfo->total_bytes.
*
* Here we make sure if we mark this bg RO, we still have enough
* free space as buffer.
* Data never overcommits, even in mixed mode, so do just the straight
* check of left over space in how much we have allocated.
*/
if (sinfo_used + num_bytes <= sinfo->total_bytes) {
if (force) {
ret = 0;
} else if (sinfo->flags & BTRFS_BLOCK_GROUP_DATA) {
u64 sinfo_used = btrfs_space_info_used(sinfo, true);

/*
* Here we make sure if we mark this bg RO, we still have enough
* free space as buffer.
*/
if (sinfo_used + num_bytes <= sinfo->total_bytes)
ret = 0;
} else {
/*
* We overcommit metadata, so we need to do the
* btrfs_can_overcommit check here, and we need to pass in
* BTRFS_RESERVE_NO_FLUSH to give ourselves the most amount of
* leeway to allow us to mark this block group as read only.
*/
if (btrfs_can_overcommit(cache->fs_info, sinfo, num_bytes,
BTRFS_RESERVE_NO_FLUSH))
ret = 0;
}

if (!ret) {
sinfo->bytes_readonly += num_bytes;
cache->ro++;
list_add_tail(&cache->ro_list, &sinfo->ro_bgs);
ret = 0;
}
out:
spin_unlock(&cache->lock);
spin_unlock(&sinfo->lock);
if (ret == -ENOSPC && btrfs_test_opt(cache->fs_info, ENOSPC_DEBUG)) {
btrfs_info(cache->fs_info,
"unable to make block group %llu ro", cache->start);
btrfs_info(cache->fs_info,
"sinfo_used=%llu bg_num_bytes=%llu",
sinfo_used, num_bytes);
btrfs_dump_space_info(cache->fs_info, cache->space_info, 0, 0);
}
return ret;
Expand Down Expand Up @@ -2225,7 +2240,7 @@ int btrfs_inc_block_group_ro(struct btrfs_block_group *cache,
}
}

ret = inc_block_group_ro(cache, !do_chunk_alloc);
ret = inc_block_group_ro(cache, 0);
if (!do_chunk_alloc)
goto unlock_out;
if (!ret)
Expand Down
8 changes: 2 additions & 6 deletions fs/btrfs/ctree.c
Original file line number Diff line number Diff line change
Expand Up @@ -326,12 +326,10 @@ u64 btrfs_get_tree_mod_seq(struct btrfs_fs_info *fs_info,
struct seq_list *elem)
{
write_lock(&fs_info->tree_mod_log_lock);
spin_lock(&fs_info->tree_mod_seq_lock);
if (!elem->seq) {
elem->seq = btrfs_inc_tree_mod_seq(fs_info);
list_add_tail(&elem->list, &fs_info->tree_mod_seq_list);
}
spin_unlock(&fs_info->tree_mod_seq_lock);
write_unlock(&fs_info->tree_mod_log_lock);

return elem->seq;
Expand All @@ -351,7 +349,7 @@ void btrfs_put_tree_mod_seq(struct btrfs_fs_info *fs_info,
if (!seq_putting)
return;

spin_lock(&fs_info->tree_mod_seq_lock);
write_lock(&fs_info->tree_mod_log_lock);
list_del(&elem->list);
elem->seq = 0;

Expand All @@ -362,19 +360,17 @@ void btrfs_put_tree_mod_seq(struct btrfs_fs_info *fs_info,
* blocker with lower sequence number exists, we
* cannot remove anything from the log
*/
spin_unlock(&fs_info->tree_mod_seq_lock);
write_unlock(&fs_info->tree_mod_log_lock);
return;
}
min_seq = cur_elem->seq;
}
}
spin_unlock(&fs_info->tree_mod_seq_lock);

/*
* anything that's lower than the lowest existing (read: blocked)
* sequence number can be removed from the tree.
*/
write_lock(&fs_info->tree_mod_log_lock);
tm_root = &fs_info->tree_mod_log;
for (node = rb_first(tm_root); node; node = next) {
next = rb_next(node);
Expand Down
6 changes: 2 additions & 4 deletions fs/btrfs/ctree.h
Original file line number Diff line number Diff line change
Expand Up @@ -714,14 +714,12 @@ struct btrfs_fs_info {
atomic_t nr_delayed_iputs;
wait_queue_head_t delayed_iputs_wait;

/* this protects tree_mod_seq_list */
spinlock_t tree_mod_seq_lock;
atomic64_t tree_mod_seq;
struct list_head tree_mod_seq_list;

/* this protects tree_mod_log */
/* this protects tree_mod_log and tree_mod_seq_list */
rwlock_t tree_mod_log_lock;
struct rb_root tree_mod_log;
struct list_head tree_mod_seq_list;

atomic_t async_delalloc_pages;

Expand Down
8 changes: 4 additions & 4 deletions fs/btrfs/delayed-ref.c
Original file line number Diff line number Diff line change
Expand Up @@ -492,15 +492,15 @@ void btrfs_merge_delayed_refs(struct btrfs_trans_handle *trans,
if (head->is_data)
return;

spin_lock(&fs_info->tree_mod_seq_lock);
read_lock(&fs_info->tree_mod_log_lock);
if (!list_empty(&fs_info->tree_mod_seq_list)) {
struct seq_list *elem;

elem = list_first_entry(&fs_info->tree_mod_seq_list,
struct seq_list, list);
seq = elem->seq;
}
spin_unlock(&fs_info->tree_mod_seq_lock);
read_unlock(&fs_info->tree_mod_log_lock);

again:
for (node = rb_first_cached(&head->ref_tree); node;
Expand All @@ -518,7 +518,7 @@ int btrfs_check_delayed_seq(struct btrfs_fs_info *fs_info, u64 seq)
struct seq_list *elem;
int ret = 0;

spin_lock(&fs_info->tree_mod_seq_lock);
read_lock(&fs_info->tree_mod_log_lock);
if (!list_empty(&fs_info->tree_mod_seq_list)) {
elem = list_first_entry(&fs_info->tree_mod_seq_list,
struct seq_list, list);
Expand All @@ -531,7 +531,7 @@ int btrfs_check_delayed_seq(struct btrfs_fs_info *fs_info, u64 seq)
}
}

spin_unlock(&fs_info->tree_mod_seq_lock);
read_unlock(&fs_info->tree_mod_log_lock);
return ret;
}

Expand Down
1 change: 0 additions & 1 deletion fs/btrfs/disk-io.c
Original file line number Diff line number Diff line change
Expand Up @@ -2697,7 +2697,6 @@ int __cold open_ctree(struct super_block *sb,
spin_lock_init(&fs_info->fs_roots_radix_lock);
spin_lock_init(&fs_info->delayed_iput_lock);
spin_lock_init(&fs_info->defrag_inodes_lock);
spin_lock_init(&fs_info->tree_mod_seq_lock);
spin_lock_init(&fs_info->super_lock);
spin_lock_init(&fs_info->buffer_lock);
spin_lock_init(&fs_info->unused_bgs_lock);
Expand Down
49 changes: 29 additions & 20 deletions fs/btrfs/extent_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -1593,21 +1593,25 @@ void find_first_clear_extent_bit(struct extent_io_tree *tree, u64 start,
/* Find first extent with bits cleared */
while (1) {
node = __etree_search(tree, start, &next, &prev, NULL, NULL);
if (!node) {
if (!node && !next && !prev) {
/*
* Tree is completely empty, send full range and let
* caller deal with it
*/
*start_ret = 0;
*end_ret = -1;
goto out;
} else if (!node && !next) {
/*
* We are past the last allocated chunk, set start at
* the end of the last extent.
*/
state = rb_entry(prev, struct extent_state, rb_node);
*start_ret = state->end + 1;
*end_ret = -1;
goto out;
} else if (!node) {
node = next;
if (!node) {
/*
* We are past the last allocated chunk,
* set start at the end of the last extent. The
* device alloc tree should never be empty so
* prev is always set.
*/
ASSERT(prev);
state = rb_entry(prev, struct extent_state, rb_node);
*start_ret = state->end + 1;
*end_ret = -1;
goto out;
}
}
/*
* At this point 'node' either contains 'start' or start is
Expand Down Expand Up @@ -3438,11 +3442,7 @@ static noinline_for_stack int __extent_writepage_io(struct inode *inode,
ret = btrfs_writepage_cow_fixup(page, start, page_end);
if (ret) {
/* Fixup worker will requeue */
if (ret == -EBUSY)
wbc->pages_skipped++;
else
redirty_page_for_writepage(wbc, page);

redirty_page_for_writepage(wbc, page);
update_nr_written(wbc, nr_written);
unlock_page(page);
return 1;
Expand Down Expand Up @@ -4166,7 +4166,16 @@ static int extent_write_cache_pages(struct address_space *mapping,
*/
scanned = 1;
index = 0;
goto retry;

/*
* If we're looping we could run into a page that is locked by a
* writer and that writer could be waiting on writeback for a
* page in our current bio, and thus deadlock, so flush the
* write bio here.
*/
ret = flush_write_bio(epd);
if (!ret)
goto retry;
}

if (wbc->range_cyclic || (wbc->nr_to_write > 0 && range_whole))
Expand Down
Loading

0 comments on commit ad80142

Please sign in to comment.