Skip to content

Commit 0a0e8b8

Browse files
Qu Wenruomasoncl
authored andcommitted
btrfs: qgroup: Don't copy extent buffer to do qgroup rescan
Ancient qgroup code call memcpy() on a extent buffer and use it for leaf iteration. As extent buffer contains lock, pointers to pages, it's never sane to do such copy. The following bug may be caused by this insane operation: [92098.841309] general protection fault: 0000 [#1] SMP [92098.841338] Modules linked in: ... [92098.841814] CPU: 1 PID: 24655 Comm: kworker/u4:12 Not tainted 4.3.0-rc1 #1 [92098.841868] Workqueue: btrfs-qgroup-rescan btrfs_qgroup_rescan_helper [btrfs] [92098.842261] Call Trace: [92098.842277] [<ffffffffc035a5d8>] ? read_extent_buffer+0xb8/0x110 [btrfs] [92098.842304] [<ffffffffc0396d00>] ? btrfs_find_all_roots+0x60/0x70 [btrfs] [92098.842329] [<ffffffffc039af3d>] btrfs_qgroup_rescan_worker+0x28d/0x5a0 [btrfs] Where btrfs_qgroup_rescan_worker+0x28d is btrfs_disk_key_to_cpu(), called in reading key from the copied extent_buffer. This patch will use btrfs_clone_extent_buffer() to a better copy of extent buffer to deal such case. Reported-by: Stephane Lesimple <[email protected]> Suggested-by: Filipe Manana <[email protected]> Signed-off-by: Qu Wenruo <[email protected]> Reviewed-by: Filipe Manana <[email protected]> Signed-off-by: Chris Mason <[email protected]>
1 parent b66d62b commit 0a0e8b8

File tree

1 file changed

+16
-10
lines changed

1 file changed

+16
-10
lines changed

fs/btrfs/qgroup.c

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2192,10 +2192,10 @@ void assert_qgroups_uptodate(struct btrfs_trans_handle *trans)
21922192
*/
21932193
static int
21942194
qgroup_rescan_leaf(struct btrfs_fs_info *fs_info, struct btrfs_path *path,
2195-
struct btrfs_trans_handle *trans,
2196-
struct extent_buffer *scratch_leaf)
2195+
struct btrfs_trans_handle *trans)
21972196
{
21982197
struct btrfs_key found;
2198+
struct extent_buffer *scratch_leaf = NULL;
21992199
struct ulist *roots = NULL;
22002200
struct seq_list tree_mod_seq_elem = SEQ_LIST_INIT(tree_mod_seq_elem);
22012201
u64 num_bytes;
@@ -2233,7 +2233,15 @@ qgroup_rescan_leaf(struct btrfs_fs_info *fs_info, struct btrfs_path *path,
22332233
fs_info->qgroup_rescan_progress.objectid = found.objectid + 1;
22342234

22352235
btrfs_get_tree_mod_seq(fs_info, &tree_mod_seq_elem);
2236-
memcpy(scratch_leaf, path->nodes[0], sizeof(*scratch_leaf));
2236+
scratch_leaf = btrfs_clone_extent_buffer(path->nodes[0]);
2237+
if (!scratch_leaf) {
2238+
ret = -ENOMEM;
2239+
mutex_unlock(&fs_info->qgroup_rescan_lock);
2240+
goto out;
2241+
}
2242+
extent_buffer_get(scratch_leaf);
2243+
btrfs_tree_read_lock(scratch_leaf);
2244+
btrfs_set_lock_blocking_rw(scratch_leaf, BTRFS_READ_LOCK);
22372245
slot = path->slots[0];
22382246
btrfs_release_path(path);
22392247
mutex_unlock(&fs_info->qgroup_rescan_lock);
@@ -2259,6 +2267,10 @@ qgroup_rescan_leaf(struct btrfs_fs_info *fs_info, struct btrfs_path *path,
22592267
goto out;
22602268
}
22612269
out:
2270+
if (scratch_leaf) {
2271+
btrfs_tree_read_unlock_blocking(scratch_leaf);
2272+
free_extent_buffer(scratch_leaf);
2273+
}
22622274
btrfs_put_tree_mod_seq(fs_info, &tree_mod_seq_elem);
22632275

22642276
return ret;
@@ -2270,16 +2282,12 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
22702282
qgroup_rescan_work);
22712283
struct btrfs_path *path;
22722284
struct btrfs_trans_handle *trans = NULL;
2273-
struct extent_buffer *scratch_leaf = NULL;
22742285
int err = -ENOMEM;
22752286
int ret = 0;
22762287

22772288
path = btrfs_alloc_path();
22782289
if (!path)
22792290
goto out;
2280-
scratch_leaf = kmalloc(sizeof(*scratch_leaf), GFP_NOFS);
2281-
if (!scratch_leaf)
2282-
goto out;
22832291

22842292
err = 0;
22852293
while (!err) {
@@ -2291,8 +2299,7 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
22912299
if (!fs_info->quota_enabled) {
22922300
err = -EINTR;
22932301
} else {
2294-
err = qgroup_rescan_leaf(fs_info, path, trans,
2295-
scratch_leaf);
2302+
err = qgroup_rescan_leaf(fs_info, path, trans);
22962303
}
22972304
if (err > 0)
22982305
btrfs_commit_transaction(trans, fs_info->fs_root);
@@ -2301,7 +2308,6 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
23012308
}
23022309

23032310
out:
2304-
kfree(scratch_leaf);
23052311
btrfs_free_path(path);
23062312

23072313
mutex_lock(&fs_info->qgroup_rescan_lock);

0 commit comments

Comments
 (0)