diff options
author | Miao Xie <miaox@cn.fujitsu.com> | 2013-06-05 23:28:03 -0400 |
---|---|---|
committer | Josef Bacik <jbacik@fusionio.com> | 2013-07-01 08:52:25 -0400 |
commit | 5bc7247ac47cf100309e4d7e24214889c46a1636 (patch) | |
tree | 2e28c383acea82994e54c330ba7345b436b69656 | |
parent | 8c2a1a3028d560cfb95f1c583e872c65ed2f0b3d (diff) |
Btrfs: fix broken nocow after balance
Balance will create reloc_root for each fs root, and it's going to
record last_snapshot to filter shared blocks. The side effect of
setting last_snapshot is to break nocow attributes of files.
Since the extents are not shared by the relocation tree after the balance,
we can recover the old last_snapshot safely if no one snapshoted the
source tree. We fix the above problem by this way.
Reported-by: Kyle Gates <kylegates@hotmail.com>
Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
Signed-off-by: Josef Bacik <jbacik@fusionio.com>
-rw-r--r-- | fs/btrfs/relocation.c | 44 |
1 files changed, 44 insertions, 0 deletions
diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index aa559f1161df..4a404b44b0ad 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c | |||
@@ -1305,6 +1305,7 @@ static struct btrfs_root *create_reloc_root(struct btrfs_trans_handle *trans, | |||
1305 | struct extent_buffer *eb; | 1305 | struct extent_buffer *eb; |
1306 | struct btrfs_root_item *root_item; | 1306 | struct btrfs_root_item *root_item; |
1307 | struct btrfs_key root_key; | 1307 | struct btrfs_key root_key; |
1308 | u64 last_snap = 0; | ||
1308 | int ret; | 1309 | int ret; |
1309 | 1310 | ||
1310 | root_item = kmalloc(sizeof(*root_item), GFP_NOFS); | 1311 | root_item = kmalloc(sizeof(*root_item), GFP_NOFS); |
@@ -1320,6 +1321,7 @@ static struct btrfs_root *create_reloc_root(struct btrfs_trans_handle *trans, | |||
1320 | BTRFS_TREE_RELOC_OBJECTID); | 1321 | BTRFS_TREE_RELOC_OBJECTID); |
1321 | BUG_ON(ret); | 1322 | BUG_ON(ret); |
1322 | 1323 | ||
1324 | last_snap = btrfs_root_last_snapshot(&root->root_item); | ||
1323 | btrfs_set_root_last_snapshot(&root->root_item, | 1325 | btrfs_set_root_last_snapshot(&root->root_item, |
1324 | trans->transid - 1); | 1326 | trans->transid - 1); |
1325 | } else { | 1327 | } else { |
@@ -1345,6 +1347,12 @@ static struct btrfs_root *create_reloc_root(struct btrfs_trans_handle *trans, | |||
1345 | memset(&root_item->drop_progress, 0, | 1347 | memset(&root_item->drop_progress, 0, |
1346 | sizeof(struct btrfs_disk_key)); | 1348 | sizeof(struct btrfs_disk_key)); |
1347 | root_item->drop_level = 0; | 1349 | root_item->drop_level = 0; |
1350 | /* | ||
1351 | * abuse rtransid, it is safe because it is impossible to | ||
1352 | * receive data into a relocation tree. | ||
1353 | */ | ||
1354 | btrfs_set_root_rtransid(root_item, last_snap); | ||
1355 | btrfs_set_root_otransid(root_item, trans->transid); | ||
1348 | } | 1356 | } |
1349 | 1357 | ||
1350 | btrfs_tree_unlock(eb); | 1358 | btrfs_tree_unlock(eb); |
@@ -2272,8 +2280,12 @@ void free_reloc_roots(struct list_head *list) | |||
2272 | static noinline_for_stack | 2280 | static noinline_for_stack |
2273 | int merge_reloc_roots(struct reloc_control *rc) | 2281 | int merge_reloc_roots(struct reloc_control *rc) |
2274 | { | 2282 | { |
2283 | struct btrfs_trans_handle *trans; | ||
2275 | struct btrfs_root *root; | 2284 | struct btrfs_root *root; |
2276 | struct btrfs_root *reloc_root; | 2285 | struct btrfs_root *reloc_root; |
2286 | u64 last_snap; | ||
2287 | u64 otransid; | ||
2288 | u64 objectid; | ||
2277 | LIST_HEAD(reloc_roots); | 2289 | LIST_HEAD(reloc_roots); |
2278 | int found = 0; | 2290 | int found = 0; |
2279 | int ret = 0; | 2291 | int ret = 0; |
@@ -2307,12 +2319,44 @@ again: | |||
2307 | } else { | 2319 | } else { |
2308 | list_del_init(&reloc_root->root_list); | 2320 | list_del_init(&reloc_root->root_list); |
2309 | } | 2321 | } |
2322 | |||
2323 | /* | ||
2324 | * we keep the old last snapshod transid in rtranid when we | ||
2325 | * created the relocation tree. | ||
2326 | */ | ||
2327 | last_snap = btrfs_root_rtransid(&reloc_root->root_item); | ||
2328 | otransid = btrfs_root_otransid(&reloc_root->root_item); | ||
2329 | objectid = reloc_root->root_key.offset; | ||
2330 | |||
2310 | ret = btrfs_drop_snapshot(reloc_root, rc->block_rsv, 0, 1); | 2331 | ret = btrfs_drop_snapshot(reloc_root, rc->block_rsv, 0, 1); |
2311 | if (ret < 0) { | 2332 | if (ret < 0) { |
2312 | if (list_empty(&reloc_root->root_list)) | 2333 | if (list_empty(&reloc_root->root_list)) |
2313 | list_add_tail(&reloc_root->root_list, | 2334 | list_add_tail(&reloc_root->root_list, |
2314 | &reloc_roots); | 2335 | &reloc_roots); |
2315 | goto out; | 2336 | goto out; |
2337 | } else if (!ret) { | ||
2338 | /* | ||
2339 | * recover the last snapshot tranid to avoid | ||
2340 | * the space balance break NOCOW. | ||
2341 | */ | ||
2342 | root = read_fs_root(rc->extent_root->fs_info, | ||
2343 | objectid); | ||
2344 | if (IS_ERR(root)) | ||
2345 | continue; | ||
2346 | |||
2347 | if (btrfs_root_refs(&root->root_item) == 0) | ||
2348 | continue; | ||
2349 | |||
2350 | trans = btrfs_join_transaction(root); | ||
2351 | BUG_ON(IS_ERR(trans)); | ||
2352 | |||
2353 | /* Check if the fs/file tree was snapshoted or not. */ | ||
2354 | if (btrfs_root_last_snapshot(&root->root_item) == | ||
2355 | otransid - 1) | ||
2356 | btrfs_set_root_last_snapshot(&root->root_item, | ||
2357 | last_snap); | ||
2358 | |||
2359 | btrfs_end_transaction(trans, root); | ||
2316 | } | 2360 | } |
2317 | } | 2361 | } |
2318 | 2362 | ||