Commit 318adf8e authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for-5.2-rc2-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux

Pull btrfs fixes from David Sterba:
 "A few more fixes for bugs reported by users, fuzzing tools and
  regressions:

   - fix crashes in relocation:
       + resuming interrupted balance operation does not properly clean
         up orphan trees
       + with enabled qgroups, resuming needs to be more careful about
         block groups due to limited context when updating qgroups

   - fsync and logging fixes found by fuzzing

   - incremental send fixes for no-holes and clone

   - fix spin lock type used in timer function for zstd"

* tag 'for-5.2-rc2-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux:
  Btrfs: fix race updating log root item during fsync
  Btrfs: fix wrong ctime and mtime of a directory after log replay
  Btrfs: fix fsync not persisting changed attributes of a directory
  btrfs: qgroup: Check bg while resuming relocation to avoid NULL pointer dereference
  btrfs: reloc: Also queue orphan reloc tree for cleanup to avoid BUG_ON()
  Btrfs: incremental send, fix emission of invalid clone operations
  Btrfs: incremental send, fix file corruption when no-holes feature is enabled
  btrfs: correct zstd workspace manager lock to use spin_lock_bh()
  btrfs: Ensure replaced device doesn't have pending chunk allocation
parents 8cb7104d 06989c79
...@@ -603,6 +603,12 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info, ...@@ -603,6 +603,12 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
} }
btrfs_wait_ordered_roots(fs_info, U64_MAX, 0, (u64)-1); btrfs_wait_ordered_roots(fs_info, U64_MAX, 0, (u64)-1);
/*
* We have to use this loop approach because at this point src_device
* has to be available for transaction commit to complete, yet new
* chunks shouldn't be allocated on the device.
*/
while (1) {
trans = btrfs_start_transaction(root, 0); trans = btrfs_start_transaction(root, 0);
if (IS_ERR(trans)) { if (IS_ERR(trans)) {
mutex_unlock(&dev_replace->lock_finishing_cancel_unmount); mutex_unlock(&dev_replace->lock_finishing_cancel_unmount);
...@@ -611,9 +617,19 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info, ...@@ -611,9 +617,19 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
ret = btrfs_commit_transaction(trans); ret = btrfs_commit_transaction(trans);
WARN_ON(ret); WARN_ON(ret);
/* keep away write_all_supers() during the finishing procedure */ /* Prevent write_all_supers() during the finishing procedure */
mutex_lock(&fs_info->fs_devices->device_list_mutex); mutex_lock(&fs_info->fs_devices->device_list_mutex);
/* Prevent new chunks being allocated on the source device */
mutex_lock(&fs_info->chunk_mutex); mutex_lock(&fs_info->chunk_mutex);
if (!list_empty(&src_device->post_commit_list)) {
mutex_unlock(&fs_info->fs_devices->device_list_mutex);
mutex_unlock(&fs_info->chunk_mutex);
} else {
break;
}
}
down_write(&dev_replace->rwsem); down_write(&dev_replace->rwsem);
dev_replace->replace_state = dev_replace->replace_state =
scrub_ret ? BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED scrub_ret ? BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED
...@@ -662,7 +678,6 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info, ...@@ -662,7 +678,6 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
btrfs_device_set_disk_total_bytes(tgt_device, btrfs_device_set_disk_total_bytes(tgt_device,
src_device->disk_total_bytes); src_device->disk_total_bytes);
btrfs_device_set_bytes_used(tgt_device, src_device->bytes_used); btrfs_device_set_bytes_used(tgt_device, src_device->bytes_used);
ASSERT(list_empty(&src_device->post_commit_list));
tgt_device->commit_total_bytes = src_device->commit_total_bytes; tgt_device->commit_total_bytes = src_device->commit_total_bytes;
tgt_device->commit_bytes_used = src_device->bytes_used; tgt_device->commit_bytes_used = src_device->bytes_used;
......
...@@ -6433,8 +6433,18 @@ int btrfs_add_link(struct btrfs_trans_handle *trans, ...@@ -6433,8 +6433,18 @@ int btrfs_add_link(struct btrfs_trans_handle *trans,
btrfs_i_size_write(parent_inode, parent_inode->vfs_inode.i_size + btrfs_i_size_write(parent_inode, parent_inode->vfs_inode.i_size +
name_len * 2); name_len * 2);
inode_inc_iversion(&parent_inode->vfs_inode); inode_inc_iversion(&parent_inode->vfs_inode);
parent_inode->vfs_inode.i_mtime = parent_inode->vfs_inode.i_ctime = /*
current_time(&parent_inode->vfs_inode); * If we are replaying a log tree, we do not want to update the mtime
* and ctime of the parent directory with the current time, since the
* log replay procedure is responsible for setting them to their correct
* values (the ones it had when the fsync was done).
*/
if (!test_bit(BTRFS_FS_LOG_RECOVERING, &root->fs_info->flags)) {
struct timespec64 now = current_time(&parent_inode->vfs_inode);
parent_inode->vfs_inode.i_mtime = now;
parent_inode->vfs_inode.i_ctime = now;
}
ret = btrfs_update_inode(trans, root, &parent_inode->vfs_inode); ret = btrfs_update_inode(trans, root, &parent_inode->vfs_inode);
if (ret) if (ret)
btrfs_abort_transaction(trans, ret); btrfs_abort_transaction(trans, ret);
......
...@@ -3830,7 +3830,13 @@ int btrfs_qgroup_add_swapped_blocks(struct btrfs_trans_handle *trans, ...@@ -3830,7 +3830,13 @@ int btrfs_qgroup_add_swapped_blocks(struct btrfs_trans_handle *trans,
subvol_slot); subvol_slot);
block->last_snapshot = last_snapshot; block->last_snapshot = last_snapshot;
block->level = level; block->level = level;
if (bg->flags & BTRFS_BLOCK_GROUP_DATA)
/*
* If we have bg == NULL, we're called from btrfs_recover_relocation(),
* no one else can modify tree blocks thus we qgroup will not change
* no matter the value of trace_leaf.
*/
if (bg && bg->flags & BTRFS_BLOCK_GROUP_DATA)
block->trace_leaf = true; block->trace_leaf = true;
else else
block->trace_leaf = false; block->trace_leaf = false;
......
...@@ -2177,22 +2177,30 @@ static int clean_dirty_subvols(struct reloc_control *rc) ...@@ -2177,22 +2177,30 @@ static int clean_dirty_subvols(struct reloc_control *rc)
struct btrfs_root *root; struct btrfs_root *root;
struct btrfs_root *next; struct btrfs_root *next;
int ret = 0; int ret = 0;
int ret2;
list_for_each_entry_safe(root, next, &rc->dirty_subvol_roots, list_for_each_entry_safe(root, next, &rc->dirty_subvol_roots,
reloc_dirty_list) { reloc_dirty_list) {
if (root->root_key.objectid != BTRFS_TREE_RELOC_OBJECTID) {
/* Merged subvolume, cleanup its reloc root */
struct btrfs_root *reloc_root = root->reloc_root; struct btrfs_root *reloc_root = root->reloc_root;
clear_bit(BTRFS_ROOT_DEAD_RELOC_TREE, &root->state); clear_bit(BTRFS_ROOT_DEAD_RELOC_TREE, &root->state);
list_del_init(&root->reloc_dirty_list); list_del_init(&root->reloc_dirty_list);
root->reloc_root = NULL; root->reloc_root = NULL;
if (reloc_root) { if (reloc_root) {
int ret2;
ret2 = btrfs_drop_snapshot(reloc_root, NULL, 0, 1); ret2 = btrfs_drop_snapshot(reloc_root, NULL, 0, 1);
if (ret2 < 0 && !ret) if (ret2 < 0 && !ret)
ret = ret2; ret = ret2;
} }
btrfs_put_fs_root(root); btrfs_put_fs_root(root);
} else {
/* Orphan reloc tree, just clean it up */
ret2 = btrfs_drop_snapshot(root, NULL, 0, 1);
if (ret2 < 0 && !ret)
ret = ret2;
}
} }
return ret; return ret;
} }
...@@ -2480,6 +2488,9 @@ void merge_reloc_roots(struct reloc_control *rc) ...@@ -2480,6 +2488,9 @@ void merge_reloc_roots(struct reloc_control *rc)
} }
} else { } else {
list_del_init(&reloc_root->root_list); list_del_init(&reloc_root->root_list);
/* Don't forget to queue this reloc root for cleanup */
list_add_tail(&reloc_root->reloc_dirty_list,
&rc->dirty_subvol_roots);
} }
} }
......
...@@ -4999,6 +4999,12 @@ static int send_hole(struct send_ctx *sctx, u64 end) ...@@ -4999,6 +4999,12 @@ static int send_hole(struct send_ctx *sctx, u64 end)
if (offset >= sctx->cur_inode_size) if (offset >= sctx->cur_inode_size)
return 0; return 0;
/*
* Don't go beyond the inode's i_size due to prealloc extents that start
* after the i_size.
*/
end = min_t(u64, end, sctx->cur_inode_size);
if (sctx->flags & BTRFS_SEND_FLAG_NO_FILE_DATA) if (sctx->flags & BTRFS_SEND_FLAG_NO_FILE_DATA)
return send_update_extent(sctx, offset, end - offset); return send_update_extent(sctx, offset, end - offset);
...@@ -5218,10 +5224,50 @@ static int clone_range(struct send_ctx *sctx, ...@@ -5218,10 +5224,50 @@ static int clone_range(struct send_ctx *sctx,
clone_len = min_t(u64, ext_len, len); clone_len = min_t(u64, ext_len, len);
if (btrfs_file_extent_disk_bytenr(leaf, ei) == disk_byte && if (btrfs_file_extent_disk_bytenr(leaf, ei) == disk_byte &&
clone_data_offset == data_offset) clone_data_offset == data_offset) {
ret = send_clone(sctx, offset, clone_len, clone_root); const u64 src_end = clone_root->offset + clone_len;
else const u64 sectorsize = SZ_64K;
/*
* We can't clone the last block, when its size is not
* sector size aligned, into the middle of a file. If we
* do so, the receiver will get a failure (-EINVAL) when
* trying to clone or will silently corrupt the data in
* the destination file if it's on a kernel without the
* fix introduced by commit ac765f83f1397646
* ("Btrfs: fix data corruption due to cloning of eof
* block).
*
* So issue a clone of the aligned down range plus a
* regular write for the eof block, if we hit that case.
*
* Also, we use the maximum possible sector size, 64K,
* because we don't know what's the sector size of the
* filesystem that receives the stream, so we have to
* assume the largest possible sector size.
*/
if (src_end == clone_src_i_size &&
!IS_ALIGNED(src_end, sectorsize) &&
offset + clone_len < sctx->cur_inode_size) {
u64 slen;
slen = ALIGN_DOWN(src_end - clone_root->offset,
sectorsize);
if (slen > 0) {
ret = send_clone(sctx, offset, slen,
clone_root);
if (ret < 0)
goto out;
}
ret = send_extent_data(sctx, offset + slen,
clone_len - slen);
} else {
ret = send_clone(sctx, offset, clone_len,
clone_root);
}
} else {
ret = send_extent_data(sctx, offset, clone_len); ret = send_extent_data(sctx, offset, clone_len);
}
if (ret < 0) if (ret < 0)
goto out; goto out;
......
...@@ -3109,6 +3109,12 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans, ...@@ -3109,6 +3109,12 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
root->log_transid++; root->log_transid++;
log->log_transid = root->log_transid; log->log_transid = root->log_transid;
root->log_start_pid = 0; root->log_start_pid = 0;
/*
* Update or create log root item under the root's log_mutex to prevent
* races with concurrent log syncs that can lead to failure to update
* log root item because it was not created yet.
*/
ret = update_log_root(trans, log);
/* /*
* IO has been started, blocks of the log tree have WRITTEN flag set * IO has been started, blocks of the log tree have WRITTEN flag set
* in their headers. new modifications of the log will be written to * in their headers. new modifications of the log will be written to
...@@ -3128,8 +3134,6 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans, ...@@ -3128,8 +3134,6 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
mutex_unlock(&log_root_tree->log_mutex); mutex_unlock(&log_root_tree->log_mutex);
ret = update_log_root(trans, log);
mutex_lock(&log_root_tree->log_mutex); mutex_lock(&log_root_tree->log_mutex);
if (atomic_dec_and_test(&log_root_tree->log_writers)) { if (atomic_dec_and_test(&log_root_tree->log_writers)) {
/* atomic_dec_and_test implies a barrier */ /* atomic_dec_and_test implies a barrier */
...@@ -5478,7 +5482,6 @@ static noinline int check_parent_dirs_for_sync(struct btrfs_trans_handle *trans, ...@@ -5478,7 +5482,6 @@ static noinline int check_parent_dirs_for_sync(struct btrfs_trans_handle *trans,
{ {
int ret = 0; int ret = 0;
struct dentry *old_parent = NULL; struct dentry *old_parent = NULL;
struct btrfs_inode *orig_inode = inode;
/* /*
* for regular files, if its inode is already on disk, we don't * for regular files, if its inode is already on disk, we don't
...@@ -5498,16 +5501,6 @@ static noinline int check_parent_dirs_for_sync(struct btrfs_trans_handle *trans, ...@@ -5498,16 +5501,6 @@ static noinline int check_parent_dirs_for_sync(struct btrfs_trans_handle *trans,
} }
while (1) { while (1) {
/*
* If we are logging a directory then we start with our inode,
* not our parent's inode, so we need to skip setting the
* logged_trans so that further down in the log code we don't
* think this inode has already been logged.
*/
if (inode != orig_inode)
inode->logged_trans = trans->transid;
smp_mb();
if (btrfs_must_commit_transaction(trans, inode)) { if (btrfs_must_commit_transaction(trans, inode)) {
ret = 1; ret = 1;
break; break;
...@@ -6384,7 +6377,6 @@ void btrfs_record_unlink_dir(struct btrfs_trans_handle *trans, ...@@ -6384,7 +6377,6 @@ void btrfs_record_unlink_dir(struct btrfs_trans_handle *trans,
* if this directory was already logged any new * if this directory was already logged any new
* names for this file/dir will get recorded * names for this file/dir will get recorded
*/ */
smp_mb();
if (dir->logged_trans == trans->transid) if (dir->logged_trans == trans->transid)
return; return;
......
...@@ -105,10 +105,10 @@ static void zstd_reclaim_timer_fn(struct timer_list *timer) ...@@ -105,10 +105,10 @@ static void zstd_reclaim_timer_fn(struct timer_list *timer)
unsigned long reclaim_threshold = jiffies - ZSTD_BTRFS_RECLAIM_JIFFIES; unsigned long reclaim_threshold = jiffies - ZSTD_BTRFS_RECLAIM_JIFFIES;
struct list_head *pos, *next; struct list_head *pos, *next;
spin_lock(&wsm.lock); spin_lock_bh(&wsm.lock);
if (list_empty(&wsm.lru_list)) { if (list_empty(&wsm.lru_list)) {
spin_unlock(&wsm.lock); spin_unlock_bh(&wsm.lock);
return; return;
} }
...@@ -137,7 +137,7 @@ static void zstd_reclaim_timer_fn(struct timer_list *timer) ...@@ -137,7 +137,7 @@ static void zstd_reclaim_timer_fn(struct timer_list *timer)
if (!list_empty(&wsm.lru_list)) if (!list_empty(&wsm.lru_list))
mod_timer(&wsm.timer, jiffies + ZSTD_BTRFS_RECLAIM_JIFFIES); mod_timer(&wsm.timer, jiffies + ZSTD_BTRFS_RECLAIM_JIFFIES);
spin_unlock(&wsm.lock); spin_unlock_bh(&wsm.lock);
} }
/* /*
...@@ -198,7 +198,7 @@ static void zstd_cleanup_workspace_manager(void) ...@@ -198,7 +198,7 @@ static void zstd_cleanup_workspace_manager(void)
struct workspace *workspace; struct workspace *workspace;
int i; int i;
spin_lock(&wsm.lock); spin_lock_bh(&wsm.lock);
for (i = 0; i < ZSTD_BTRFS_MAX_LEVEL; i++) { for (i = 0; i < ZSTD_BTRFS_MAX_LEVEL; i++) {
while (!list_empty(&wsm.idle_ws[i])) { while (!list_empty(&wsm.idle_ws[i])) {
workspace = container_of(wsm.idle_ws[i].next, workspace = container_of(wsm.idle_ws[i].next,
...@@ -208,7 +208,7 @@ static void zstd_cleanup_workspace_manager(void) ...@@ -208,7 +208,7 @@ static void zstd_cleanup_workspace_manager(void)
zstd_free_workspace(&workspace->list); zstd_free_workspace(&workspace->list);
} }
} }
spin_unlock(&wsm.lock); spin_unlock_bh(&wsm.lock);
del_timer_sync(&wsm.timer); del_timer_sync(&wsm.timer);
} }
...@@ -230,7 +230,7 @@ static struct list_head *zstd_find_workspace(unsigned int level) ...@@ -230,7 +230,7 @@ static struct list_head *zstd_find_workspace(unsigned int level)
struct workspace *workspace; struct workspace *workspace;
int i = level - 1; int i = level - 1;
spin_lock(&wsm.lock); spin_lock_bh(&wsm.lock);
for_each_set_bit_from(i, &wsm.active_map, ZSTD_BTRFS_MAX_LEVEL) { for_each_set_bit_from(i, &wsm.active_map, ZSTD_BTRFS_MAX_LEVEL) {
if (!list_empty(&wsm.idle_ws[i])) { if (!list_empty(&wsm.idle_ws[i])) {
ws = wsm.idle_ws[i].next; ws = wsm.idle_ws[i].next;
...@@ -242,11 +242,11 @@ static struct list_head *zstd_find_workspace(unsigned int level) ...@@ -242,11 +242,11 @@ static struct list_head *zstd_find_workspace(unsigned int level)
list_del(&workspace->lru_list); list_del(&workspace->lru_list);
if (list_empty(&wsm.idle_ws[i])) if (list_empty(&wsm.idle_ws[i]))
clear_bit(i, &wsm.active_map); clear_bit(i, &wsm.active_map);
spin_unlock(&wsm.lock); spin_unlock_bh(&wsm.lock);
return ws; return ws;
} }
} }
spin_unlock(&wsm.lock); spin_unlock_bh(&wsm.lock);
return NULL; return NULL;
} }
...@@ -305,7 +305,7 @@ static void zstd_put_workspace(struct list_head *ws) ...@@ -305,7 +305,7 @@ static void zstd_put_workspace(struct list_head *ws)
{ {
struct workspace *workspace = list_to_workspace(ws); struct workspace *workspace = list_to_workspace(ws);
spin_lock(&wsm.lock); spin_lock_bh(&wsm.lock);
/* A node is only taken off the lru if we are the corresponding level */ /* A node is only taken off the lru if we are the corresponding level */
if (workspace->req_level == workspace->level) { if (workspace->req_level == workspace->level) {
...@@ -325,7 +325,7 @@ static void zstd_put_workspace(struct list_head *ws) ...@@ -325,7 +325,7 @@ static void zstd_put_workspace(struct list_head *ws)
list_add(&workspace->list, &wsm.idle_ws[workspace->level - 1]); list_add(&workspace->list, &wsm.idle_ws[workspace->level - 1]);
workspace->req_level = 0; workspace->req_level = 0;
spin_unlock(&wsm.lock); spin_unlock_bh(&wsm.lock);
if (workspace->level == ZSTD_BTRFS_MAX_LEVEL) if (workspace->level == ZSTD_BTRFS_MAX_LEVEL)
cond_wake_up(&wsm.wait); cond_wake_up(&wsm.wait);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment