diff options
| -rw-r--r-- | fs/btrfs/ctree.h | 1 | ||||
| -rw-r--r-- | fs/btrfs/disk-io.c | 1 | ||||
| -rw-r--r-- | fs/btrfs/extent-tree.c | 21 |
3 files changed, 22 insertions, 1 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 54a66fa6beb1..d3562dd43c66 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h | |||
| @@ -1744,6 +1744,7 @@ struct btrfs_fs_info { | |||
| 1744 | 1744 | ||
| 1745 | spinlock_t unused_bgs_lock; | 1745 | spinlock_t unused_bgs_lock; |
| 1746 | struct list_head unused_bgs; | 1746 | struct list_head unused_bgs; |
| 1747 | struct mutex unused_bg_unpin_mutex; | ||
| 1747 | 1748 | ||
| 1748 | /* For btrfs to record security options */ | 1749 | /* For btrfs to record security options */ |
| 1749 | struct security_mnt_opts security_opts; | 1750 | struct security_mnt_opts security_opts; |
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 263d1471d01a..41b320e235d7 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c | |||
| @@ -2242,6 +2242,7 @@ int open_ctree(struct super_block *sb, | |||
| 2242 | spin_lock_init(&fs_info->qgroup_op_lock); | 2242 | spin_lock_init(&fs_info->qgroup_op_lock); |
| 2243 | spin_lock_init(&fs_info->buffer_lock); | 2243 | spin_lock_init(&fs_info->buffer_lock); |
| 2244 | spin_lock_init(&fs_info->unused_bgs_lock); | 2244 | spin_lock_init(&fs_info->unused_bgs_lock); |
| 2245 | mutex_init(&fs_info->unused_bg_unpin_mutex); | ||
| 2245 | rwlock_init(&fs_info->tree_mod_log_lock); | 2246 | rwlock_init(&fs_info->tree_mod_log_lock); |
| 2246 | mutex_init(&fs_info->reloc_mutex); | 2247 | mutex_init(&fs_info->reloc_mutex); |
| 2247 | mutex_init(&fs_info->delalloc_root_mutex); | 2248 | mutex_init(&fs_info->delalloc_root_mutex); |
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 53294da0749d..857a859948a3 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c | |||
| @@ -5735,10 +5735,13 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, | |||
| 5735 | unpin = &fs_info->freed_extents[0]; | 5735 | unpin = &fs_info->freed_extents[0]; |
| 5736 | 5736 | ||
| 5737 | while (1) { | 5737 | while (1) { |
| 5738 | mutex_lock(&fs_info->unused_bg_unpin_mutex); | ||
| 5738 | ret = find_first_extent_bit(unpin, 0, &start, &end, | 5739 | ret = find_first_extent_bit(unpin, 0, &start, &end, |
| 5739 | EXTENT_DIRTY, NULL); | 5740 | EXTENT_DIRTY, NULL); |
| 5740 | if (ret) | 5741 | if (ret) { |
| 5742 | mutex_unlock(&fs_info->unused_bg_unpin_mutex); | ||
| 5741 | break; | 5743 | break; |
| 5744 | } | ||
| 5742 | 5745 | ||
| 5743 | if (btrfs_test_opt(root, DISCARD)) | 5746 | if (btrfs_test_opt(root, DISCARD)) |
| 5744 | ret = btrfs_discard_extent(root, start, | 5747 | ret = btrfs_discard_extent(root, start, |
| @@ -5746,6 +5749,7 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, | |||
| 5746 | 5749 | ||
| 5747 | clear_extent_dirty(unpin, start, end, GFP_NOFS); | 5750 | clear_extent_dirty(unpin, start, end, GFP_NOFS); |
| 5748 | unpin_extent_range(root, start, end, true); | 5751 | unpin_extent_range(root, start, end, true); |
| 5752 | mutex_unlock(&fs_info->unused_bg_unpin_mutex); | ||
| 5749 | cond_resched(); | 5753 | cond_resched(); |
| 5750 | } | 5754 | } |
| 5751 | 5755 | ||
| @@ -9561,18 +9565,33 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info) | |||
| 9561 | */ | 9565 | */ |
| 9562 | start = block_group->key.objectid; | 9566 | start = block_group->key.objectid; |
| 9563 | end = start + block_group->key.offset - 1; | 9567 | end = start + block_group->key.offset - 1; |
| 9568 | /* | ||
| 9569 | * Hold the unused_bg_unpin_mutex lock to avoid racing with | ||
| 9570 | * btrfs_finish_extent_commit(). If we are at transaction N, | ||
| 9571 | * another task might be running finish_extent_commit() for the | ||
| 9572 | * previous transaction N - 1, and have seen a range belonging | ||
| 9573 | * to the block group in freed_extents[] before we were able to | ||
| 9574 | * clear the whole block group range from freed_extents[]. This | ||
| 9575 | * means that task can lookup for the block group after we | ||
| 9576 | * unpinned it from freed_extents[] and removed it, leading to | ||
| 9577 | * a BUG_ON() at btrfs_unpin_extent_range(). | ||
| 9578 | */ | ||
| 9579 | mutex_lock(&fs_info->unused_bg_unpin_mutex); | ||
| 9564 | ret = clear_extent_bits(&fs_info->freed_extents[0], start, end, | 9580 | ret = clear_extent_bits(&fs_info->freed_extents[0], start, end, |
| 9565 | EXTENT_DIRTY, GFP_NOFS); | 9581 | EXTENT_DIRTY, GFP_NOFS); |
| 9566 | if (ret) { | 9582 | if (ret) { |
| 9583 | mutex_unlock(&fs_info->unused_bg_unpin_mutex); | ||
| 9567 | btrfs_set_block_group_rw(root, block_group); | 9584 | btrfs_set_block_group_rw(root, block_group); |
| 9568 | goto end_trans; | 9585 | goto end_trans; |
| 9569 | } | 9586 | } |
| 9570 | ret = clear_extent_bits(&fs_info->freed_extents[1], start, end, | 9587 | ret = clear_extent_bits(&fs_info->freed_extents[1], start, end, |
| 9571 | EXTENT_DIRTY, GFP_NOFS); | 9588 | EXTENT_DIRTY, GFP_NOFS); |
| 9572 | if (ret) { | 9589 | if (ret) { |
| 9590 | mutex_unlock(&fs_info->unused_bg_unpin_mutex); | ||
| 9573 | btrfs_set_block_group_rw(root, block_group); | 9591 | btrfs_set_block_group_rw(root, block_group); |
| 9574 | goto end_trans; | 9592 | goto end_trans; |
| 9575 | } | 9593 | } |
| 9594 | mutex_unlock(&fs_info->unused_bg_unpin_mutex); | ||
| 9576 | 9595 | ||
| 9577 | /* Reset pinned so btrfs_put_block_group doesn't complain */ | 9596 | /* Reset pinned so btrfs_put_block_group doesn't complain */ |
| 9578 | block_group->pinned = 0; | 9597 | block_group->pinned = 0; |
