diff options
Diffstat (limited to 'fs/ocfs2/refcounttree.c')
-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 | } | ||