aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorQu Wenruo <quwenruo@cn.fujitsu.com>2015-10-25 21:19:43 -0400
committerChris Mason <clm@fb.com>2015-10-26 22:42:30 -0400
commit0a0e8b89389266bed9cc074c57ea662e4b9b2621 (patch)
treedc4ba61a3abd9b9c3bf4803127c3a7337450439d
parentb66d62ba1e0d7ada5b89afffe19d12662b5c92c9 (diff)
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 <stephane_btrfs@lesimple.fr> Suggested-by: Filipe Manana <fdmanana@kernel.org> Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com> Reviewed-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: Chris Mason <clm@fb.com>
-rw-r--r--fs/btrfs/qgroup.c26
1 files changed, 16 insertions, 10 deletions
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index 158633c9bbd9..31d19344427a 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -2192,10 +2192,10 @@ void assert_qgroups_uptodate(struct btrfs_trans_handle *trans)
2192 */ 2192 */
2193static int 2193static int
2194qgroup_rescan_leaf(struct btrfs_fs_info *fs_info, struct btrfs_path *path, 2194qgroup_rescan_leaf(struct btrfs_fs_info *fs_info, struct btrfs_path *path,
2195 struct btrfs_trans_handle *trans, 2195 struct btrfs_trans_handle *trans)
2196 struct extent_buffer *scratch_leaf)
2197{ 2196{
2198 struct btrfs_key found; 2197 struct btrfs_key found;
2198 struct extent_buffer *scratch_leaf = NULL;
2199 struct ulist *roots = NULL; 2199 struct ulist *roots = NULL;
2200 struct seq_list tree_mod_seq_elem = SEQ_LIST_INIT(tree_mod_seq_elem); 2200 struct seq_list tree_mod_seq_elem = SEQ_LIST_INIT(tree_mod_seq_elem);
2201 u64 num_bytes; 2201 u64 num_bytes;
@@ -2233,7 +2233,15 @@ qgroup_rescan_leaf(struct btrfs_fs_info *fs_info, struct btrfs_path *path,
2233 fs_info->qgroup_rescan_progress.objectid = found.objectid + 1; 2233 fs_info->qgroup_rescan_progress.objectid = found.objectid + 1;
2234 2234
2235 btrfs_get_tree_mod_seq(fs_info, &tree_mod_seq_elem); 2235 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);
2237 slot = path->slots[0]; 2245 slot = path->slots[0];
2238 btrfs_release_path(path); 2246 btrfs_release_path(path);
2239 mutex_unlock(&fs_info->qgroup_rescan_lock); 2247 mutex_unlock(&fs_info->qgroup_rescan_lock);
@@ -2259,6 +2267,10 @@ qgroup_rescan_leaf(struct btrfs_fs_info *fs_info, struct btrfs_path *path,
2259 goto out; 2267 goto out;
2260 } 2268 }
2261out: 2269out:
2270 if (scratch_leaf) {
2271 btrfs_tree_read_unlock_blocking(scratch_leaf);
2272 free_extent_buffer(scratch_leaf);
2273 }
2262 btrfs_put_tree_mod_seq(fs_info, &tree_mod_seq_elem); 2274 btrfs_put_tree_mod_seq(fs_info, &tree_mod_seq_elem);
2263 2275
2264 return ret; 2276 return ret;
@@ -2270,16 +2282,12 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
2270 qgroup_rescan_work); 2282 qgroup_rescan_work);
2271 struct btrfs_path *path; 2283 struct btrfs_path *path;
2272 struct btrfs_trans_handle *trans = NULL; 2284 struct btrfs_trans_handle *trans = NULL;
2273 struct extent_buffer *scratch_leaf = NULL;
2274 int err = -ENOMEM; 2285 int err = -ENOMEM;
2275 int ret = 0; 2286 int ret = 0;
2276 2287
2277 path = btrfs_alloc_path(); 2288 path = btrfs_alloc_path();
2278 if (!path) 2289 if (!path)
2279 goto out; 2290 goto out;
2280 scratch_leaf = kmalloc(sizeof(*scratch_leaf), GFP_NOFS);
2281 if (!scratch_leaf)
2282 goto out;
2283 2291
2284 err = 0; 2292 err = 0;
2285 while (!err) { 2293 while (!err) {
@@ -2291,8 +2299,7 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
2291 if (!fs_info->quota_enabled) { 2299 if (!fs_info->quota_enabled) {
2292 err = -EINTR; 2300 err = -EINTR;
2293 } else { 2301 } else {
2294 err = qgroup_rescan_leaf(fs_info, path, trans, 2302 err = qgroup_rescan_leaf(fs_info, path, trans);
2295 scratch_leaf);
2296 } 2303 }
2297 if (err > 0) 2304 if (err > 0)
2298 btrfs_commit_transaction(trans, fs_info->fs_root); 2305 btrfs_commit_transaction(trans, fs_info->fs_root);
@@ -2301,7 +2308,6 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
2301 } 2308 }
2302 2309
2303out: 2310out:
2304 kfree(scratch_leaf);
2305 btrfs_free_path(path); 2311 btrfs_free_path(path);
2306 2312
2307 mutex_lock(&fs_info->qgroup_rescan_lock); 2313 mutex_lock(&fs_info->qgroup_rescan_lock);