diff options
| author | Tao Ma <tao.ma@oracle.com> | 2009-08-22 11:54:27 -0400 |
|---|---|---|
| committer | Joel Becker <joel.becker@oracle.com> | 2009-09-22 23:09:38 -0400 |
| commit | 110a045aca62f6f564e3b68f89af2a3a5a6ecff2 (patch) | |
| tree | d7c2df9a59e527eb14add4daf1c0107e486715f4 /fs/ocfs2 | |
| parent | 37f8a2bfaa8364dd3644cccee8824bb8f5e409a5 (diff) | |
ocfs2: Add normal functions for reflink a normal file's extents.
2 major functions are added in this patch.
ocfs2_attach_refcount_tree will create a new refcount tree to the
old file if it doesn't have one and insert all the extent records
to the tree if they are not refcounted.
ocfs2_create_reflink_node will:
1. set the refcount tree to the new file.
2. call ocfs2_duplicate_extent_list which will iterate all the
extents for the old file, insert it to the new file and increase
the corresponding referennce count.
Signed-off-by: Tao Ma <tao.ma@oracle.com>
Diffstat (limited to 'fs/ocfs2')
| -rw-r--r-- | fs/ocfs2/refcounttree.c | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c index 37aa0c8696d6..e3171c483685 100644 --- a/fs/ocfs2/refcounttree.c +++ b/fs/ocfs2/refcounttree.c | |||
| @@ -3282,3 +3282,289 @@ int ocfs2_refcount_cow(struct inode *inode, | |||
| 3282 | 3282 | ||
| 3283 | return ret; | 3283 | return ret; |
| 3284 | } | 3284 | } |
| 3285 | |||
| 3286 | /* | ||
| 3287 | * Insert a new extent into refcount tree and mark a extent rec | ||
| 3288 | * as refcounted in the dinode tree. | ||
| 3289 | */ | ||
| 3290 | int ocfs2_add_refcount_flag(struct inode *inode, | ||
| 3291 | struct ocfs2_extent_tree *data_et, | ||
| 3292 | struct ocfs2_caching_info *ref_ci, | ||
| 3293 | struct buffer_head *ref_root_bh, | ||
| 3294 | u32 cpos, u32 p_cluster, u32 num_clusters, | ||
| 3295 | struct ocfs2_cached_dealloc_ctxt *dealloc) | ||
| 3296 | { | ||
| 3297 | int ret; | ||
| 3298 | handle_t *handle; | ||
| 3299 | int credits = 1, ref_blocks = 0; | ||
| 3300 | struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); | ||
| 3301 | struct ocfs2_alloc_context *meta_ac = NULL; | ||
| 3302 | |||
| 3303 | ret = ocfs2_calc_refcount_meta_credits(inode->i_sb, | ||
| 3304 | ref_ci, ref_root_bh, | ||
| 3305 | p_cluster, num_clusters, | ||
| 3306 | &ref_blocks, &credits); | ||
| 3307 | if (ret) { | ||
| 3308 | mlog_errno(ret); | ||
| 3309 | goto out; | ||
| 3310 | } | ||
| 3311 | |||
| 3312 | mlog(0, "reserve new metadata %d, credits = %d\n", | ||
| 3313 | ref_blocks, credits); | ||
| 3314 | |||
| 3315 | if (ref_blocks) { | ||
| 3316 | ret = ocfs2_reserve_new_metadata_blocks(OCFS2_SB(inode->i_sb), | ||
| 3317 | ref_blocks, &meta_ac); | ||
| 3318 | if (ret) { | ||
| 3319 | mlog_errno(ret); | ||
| 3320 | goto out; | ||
| 3321 | } | ||
| 3322 | } | ||
| 3323 | |||
| 3324 | handle = ocfs2_start_trans(osb, credits); | ||
| 3325 | if (IS_ERR(handle)) { | ||
| 3326 | ret = PTR_ERR(handle); | ||
| 3327 | mlog_errno(ret); | ||
| 3328 | goto out; | ||
| 3329 | } | ||
| 3330 | |||
| 3331 | ret = ocfs2_mark_extent_refcounted(inode, data_et, handle, | ||
| 3332 | cpos, num_clusters, p_cluster, | ||
| 3333 | meta_ac, dealloc); | ||
| 3334 | if (ret) { | ||
| 3335 | mlog_errno(ret); | ||
| 3336 | goto out_commit; | ||
| 3337 | } | ||
| 3338 | |||
| 3339 | ret = __ocfs2_increase_refcount(handle, ref_ci, ref_root_bh, | ||
| 3340 | p_cluster, num_clusters, | ||
| 3341 | meta_ac, dealloc); | ||
| 3342 | if (ret) | ||
| 3343 | mlog_errno(ret); | ||
| 3344 | |||
| 3345 | out_commit: | ||
| 3346 | ocfs2_commit_trans(osb, handle); | ||
| 3347 | out: | ||
| 3348 | if (meta_ac) | ||
| 3349 | ocfs2_free_alloc_context(meta_ac); | ||
| 3350 | return ret; | ||
| 3351 | } | ||
| 3352 | |||
| 3353 | static int ocfs2_attach_refcount_tree(struct inode *inode, | ||
| 3354 | struct buffer_head *di_bh) | ||
| 3355 | { | ||
| 3356 | int ret; | ||
| 3357 | struct buffer_head *ref_root_bh = NULL; | ||
| 3358 | struct ocfs2_inode_info *oi = OCFS2_I(inode); | ||
| 3359 | struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; | ||
| 3360 | struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); | ||
| 3361 | struct ocfs2_refcount_tree *ref_tree; | ||
| 3362 | unsigned int ext_flags; | ||
| 3363 | loff_t size; | ||
| 3364 | u32 cpos, num_clusters, clusters, p_cluster; | ||
| 3365 | struct ocfs2_cached_dealloc_ctxt dealloc; | ||
| 3366 | struct ocfs2_extent_tree di_et; | ||
| 3367 | |||
| 3368 | ocfs2_init_dealloc_ctxt(&dealloc); | ||
| 3369 | |||
| 3370 | if (!(oi->ip_dyn_features & OCFS2_HAS_REFCOUNT_FL)) { | ||
| 3371 | ret = ocfs2_create_refcount_tree(inode, di_bh); | ||
| 3372 | if (ret) { | ||
| 3373 | mlog_errno(ret); | ||
| 3374 | goto out; | ||
| 3375 | } | ||
| 3376 | } | ||
| 3377 | |||
| 3378 | BUG_ON(!di->i_refcount_loc); | ||
| 3379 | ret = ocfs2_lock_refcount_tree(osb, | ||
| 3380 | le64_to_cpu(di->i_refcount_loc), 1, | ||
| 3381 | &ref_tree, &ref_root_bh); | ||
| 3382 | if (ret) { | ||
| 3383 | mlog_errno(ret); | ||
| 3384 | goto out; | ||
| 3385 | } | ||
| 3386 | |||
| 3387 | ocfs2_init_dinode_extent_tree(&di_et, INODE_CACHE(inode), di_bh); | ||
| 3388 | |||
| 3389 | size = i_size_read(inode); | ||
| 3390 | clusters = ocfs2_clusters_for_bytes(inode->i_sb, size); | ||
| 3391 | |||
| 3392 | cpos = 0; | ||
| 3393 | while (cpos < clusters) { | ||
| 3394 | ret = ocfs2_get_clusters(inode, cpos, &p_cluster, | ||
| 3395 | &num_clusters, &ext_flags); | ||
| 3396 | |||
| 3397 | if (p_cluster && !(ext_flags & OCFS2_EXT_REFCOUNTED)) { | ||
| 3398 | ret = ocfs2_add_refcount_flag(inode, &di_et, | ||
| 3399 | &ref_tree->rf_ci, | ||
| 3400 | ref_root_bh, cpos, | ||
| 3401 | p_cluster, num_clusters, | ||
| 3402 | &dealloc); | ||
| 3403 | if (ret) { | ||
| 3404 | mlog_errno(ret); | ||
| 3405 | break; | ||
| 3406 | } | ||
| 3407 | } | ||
| 3408 | cpos += num_clusters; | ||
| 3409 | } | ||
| 3410 | |||
| 3411 | ocfs2_unlock_refcount_tree(osb, ref_tree, 1); | ||
| 3412 | brelse(ref_root_bh); | ||
| 3413 | |||
| 3414 | if (!ret && ocfs2_dealloc_has_cluster(&dealloc)) { | ||
| 3415 | ocfs2_schedule_truncate_log_flush(osb, 1); | ||
| 3416 | ocfs2_run_deallocs(osb, &dealloc); | ||
| 3417 | } | ||
| 3418 | out: | ||
| 3419 | /* | ||
| 3420 | * Empty the extent map so that we may get the right extent | ||
| 3421 | * record from the disk. | ||
| 3422 | */ | ||
| 3423 | ocfs2_extent_map_trunc(inode, 0); | ||
| 3424 | |||
| 3425 | return ret; | ||
| 3426 | } | ||
| 3427 | |||
| 3428 | static int ocfs2_add_refcounted_extent(struct inode *inode, | ||
| 3429 | struct ocfs2_extent_tree *et, | ||
| 3430 | struct ocfs2_caching_info *ref_ci, | ||
| 3431 | struct buffer_head *ref_root_bh, | ||
| 3432 | u32 cpos, u32 p_cluster, u32 num_clusters, | ||
| 3433 | unsigned int ext_flags, | ||
| 3434 | struct ocfs2_cached_dealloc_ctxt *dealloc) | ||
| 3435 | { | ||
| 3436 | int ret; | ||
| 3437 | handle_t *handle; | ||
| 3438 | int credits = 0; | ||
| 3439 | struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); | ||
| 3440 | struct ocfs2_alloc_context *meta_ac = NULL; | ||
| 3441 | |||
| 3442 | ret = ocfs2_lock_refcount_allocators(inode->i_sb, | ||
| 3443 | p_cluster, num_clusters, | ||
| 3444 | et, ref_ci, | ||
| 3445 | ref_root_bh, &meta_ac, | ||
| 3446 | NULL, &credits); | ||
| 3447 | if (ret) { | ||
| 3448 | mlog_errno(ret); | ||
| 3449 | goto out; | ||
| 3450 | } | ||
| 3451 | |||
| 3452 | handle = ocfs2_start_trans(osb, credits); | ||
| 3453 | if (IS_ERR(handle)) { | ||
| 3454 | ret = PTR_ERR(handle); | ||
| 3455 | mlog_errno(ret); | ||
| 3456 | goto out; | ||
| 3457 | } | ||
| 3458 | |||
| 3459 | ret = ocfs2_insert_extent(handle, et, cpos, | ||
| 3460 | cpu_to_le64(ocfs2_clusters_to_blocks(inode->i_sb, | ||
| 3461 | p_cluster)), | ||
| 3462 | num_clusters, ext_flags, meta_ac); | ||
| 3463 | if (ret) { | ||
| 3464 | mlog_errno(ret); | ||
| 3465 | goto out_commit; | ||
| 3466 | } | ||
| 3467 | |||
| 3468 | ret = __ocfs2_increase_refcount(handle, ref_ci, ref_root_bh, | ||
| 3469 | p_cluster, num_clusters, | ||
| 3470 | meta_ac, dealloc); | ||
| 3471 | if (ret) | ||
| 3472 | mlog_errno(ret); | ||
| 3473 | |||
| 3474 | out_commit: | ||
| 3475 | ocfs2_commit_trans(osb, handle); | ||
| 3476 | out: | ||
| 3477 | if (meta_ac) | ||
| 3478 | ocfs2_free_alloc_context(meta_ac); | ||
| 3479 | return ret; | ||
| 3480 | } | ||
| 3481 | |||
| 3482 | static int ocfs2_duplicate_extent_list(struct inode *s_inode, | ||
| 3483 | struct inode *t_inode, | ||
| 3484 | struct buffer_head *t_bh, | ||
| 3485 | struct ocfs2_caching_info *ref_ci, | ||
| 3486 | struct buffer_head *ref_root_bh, | ||
| 3487 | struct ocfs2_cached_dealloc_ctxt *dealloc) | ||
| 3488 | { | ||
| 3489 | int ret = 0; | ||
| 3490 | u32 p_cluster, num_clusters, clusters, cpos; | ||
| 3491 | loff_t size; | ||
| 3492 | unsigned int ext_flags; | ||
| 3493 | struct ocfs2_extent_tree et; | ||
| 3494 | |||
| 3495 | ocfs2_init_dinode_extent_tree(&et, INODE_CACHE(t_inode), t_bh); | ||
| 3496 | |||
| 3497 | size = i_size_read(s_inode); | ||
| 3498 | clusters = ocfs2_clusters_for_bytes(s_inode->i_sb, size); | ||
| 3499 | |||
| 3500 | cpos = 0; | ||
| 3501 | while (cpos < clusters) { | ||
| 3502 | ret = ocfs2_get_clusters(s_inode, cpos, &p_cluster, | ||
| 3503 | &num_clusters, &ext_flags); | ||
| 3504 | |||
| 3505 | if (p_cluster) { | ||
| 3506 | ret = ocfs2_add_refcounted_extent(t_inode, &et, | ||
| 3507 | ref_ci, ref_root_bh, | ||
| 3508 | cpos, p_cluster, | ||
| 3509 | num_clusters, | ||
| 3510 | ext_flags, | ||
| 3511 | dealloc); | ||
| 3512 | if (ret) { | ||
| 3513 | mlog_errno(ret); | ||
| 3514 | goto out; | ||
| 3515 | } | ||
| 3516 | } | ||
| 3517 | |||
| 3518 | cpos += num_clusters; | ||
| 3519 | } | ||
| 3520 | |||
| 3521 | out: | ||
| 3522 | return ret; | ||
| 3523 | } | ||
| 3524 | |||
| 3525 | static int ocfs2_create_reflink_node(struct inode *s_inode, | ||
| 3526 | struct buffer_head *s_bh, | ||
| 3527 | struct inode *t_inode, | ||
| 3528 | struct buffer_head *t_bh) | ||
| 3529 | { | ||
| 3530 | int ret; | ||
| 3531 | struct buffer_head *ref_root_bh = NULL; | ||
| 3532 | struct ocfs2_cached_dealloc_ctxt dealloc; | ||
| 3533 | struct ocfs2_super *osb = OCFS2_SB(s_inode->i_sb); | ||
| 3534 | struct ocfs2_refcount_block *rb; | ||
| 3535 | struct ocfs2_dinode *di = (struct ocfs2_dinode *)s_bh->b_data; | ||
| 3536 | struct ocfs2_refcount_tree *ref_tree; | ||
| 3537 | |||
| 3538 | ocfs2_init_dealloc_ctxt(&dealloc); | ||
| 3539 | |||
| 3540 | ret = ocfs2_set_refcount_tree(t_inode, t_bh, | ||
| 3541 | le64_to_cpu(di->i_refcount_loc)); | ||
| 3542 | if (ret) { | ||
| 3543 | mlog_errno(ret); | ||
| 3544 | goto out; | ||
| 3545 | } | ||
| 3546 | |||
| 3547 | ret = ocfs2_lock_refcount_tree(osb, le64_to_cpu(di->i_refcount_loc), | ||
| 3548 | 1, &ref_tree, &ref_root_bh); | ||
| 3549 | if (ret) { | ||
| 3550 | mlog_errno(ret); | ||
| 3551 | goto out; | ||
| 3552 | } | ||
| 3553 | rb = (struct ocfs2_refcount_block *)ref_root_bh->b_data; | ||
| 3554 | |||
| 3555 | ret = ocfs2_duplicate_extent_list(s_inode, t_inode, t_bh, | ||
| 3556 | &ref_tree->rf_ci, ref_root_bh, | ||
| 3557 | &dealloc); | ||
| 3558 | if (ret) | ||
| 3559 | mlog_errno(ret); | ||
| 3560 | |||
| 3561 | ocfs2_unlock_refcount_tree(osb, ref_tree, 1); | ||
| 3562 | brelse(ref_root_bh); | ||
| 3563 | out: | ||
| 3564 | if (ocfs2_dealloc_has_cluster(&dealloc)) { | ||
| 3565 | ocfs2_schedule_truncate_log_flush(osb, 1); | ||
| 3566 | ocfs2_run_deallocs(osb, &dealloc); | ||
| 3567 | } | ||
| 3568 | |||
| 3569 | return ret; | ||
| 3570 | } | ||
