diff options
author | Tao Ma <tao.ma@oracle.com> | 2009-08-17 23:43:17 -0400 |
---|---|---|
committer | Joel Becker <joel.becker@oracle.com> | 2009-09-22 23:09:41 -0400 |
commit | 492a8a33e1cb966fa0b5756c5fc11d30c8f8848e (patch) | |
tree | eacf691b07c8fa3e81c11ff1d6b8773c2dbb9e77 /fs/ocfs2/refcounttree.c | |
parent | 913580b4cd445c4fb25d7cf167911a8cf6bdb1eb (diff) |
ocfs2: Add CoW support for xattr.
In order to make 2 transcation(xattr and cow) independent with each other,
we CoW the whole xattr out in case we are setting them.
Signed-off-by: Tao Ma <tao.ma@oracle.com>
Diffstat (limited to 'fs/ocfs2/refcounttree.c')
-rw-r--r-- | fs/ocfs2/refcounttree.c | 246 |
1 files changed, 243 insertions, 3 deletions
diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c index 40de7bb9e9a6..a5b5bef054a7 100644 --- a/fs/ocfs2/refcounttree.c +++ b/fs/ocfs2/refcounttree.c | |||
@@ -32,6 +32,7 @@ | |||
32 | #include "dlmglue.h" | 32 | #include "dlmglue.h" |
33 | #include "extent_map.h" | 33 | #include "extent_map.h" |
34 | #include "aops.h" | 34 | #include "aops.h" |
35 | #include "xattr.h" | ||
35 | 36 | ||
36 | #include <linux/bio.h> | 37 | #include <linux/bio.h> |
37 | #include <linux/blkdev.h> | 38 | #include <linux/blkdev.h> |
@@ -51,6 +52,9 @@ struct ocfs2_cow_context { | |||
51 | struct ocfs2_alloc_context *meta_ac; | 52 | struct ocfs2_alloc_context *meta_ac; |
52 | struct ocfs2_alloc_context *data_ac; | 53 | struct ocfs2_alloc_context *data_ac; |
53 | struct ocfs2_cached_dealloc_ctxt dealloc; | 54 | struct ocfs2_cached_dealloc_ctxt dealloc; |
55 | void *cow_object; | ||
56 | struct ocfs2_post_refcount *post_refcount; | ||
57 | int extra_credits; | ||
54 | int (*get_clusters)(struct ocfs2_cow_context *context, | 58 | int (*get_clusters)(struct ocfs2_cow_context *context, |
55 | u32 v_cluster, u32 *p_cluster, | 59 | u32 v_cluster, u32 *p_cluster, |
56 | u32 *num_clusters, | 60 | u32 *num_clusters, |
@@ -2848,6 +2852,65 @@ unlock: | |||
2848 | return ret; | 2852 | return ret; |
2849 | } | 2853 | } |
2850 | 2854 | ||
2855 | static int ocfs2_duplicate_clusters_by_jbd(handle_t *handle, | ||
2856 | struct ocfs2_cow_context *context, | ||
2857 | u32 cpos, u32 old_cluster, | ||
2858 | u32 new_cluster, u32 new_len) | ||
2859 | { | ||
2860 | int ret = 0; | ||
2861 | struct super_block *sb = context->inode->i_sb; | ||
2862 | struct ocfs2_caching_info *ci = context->data_et.et_ci; | ||
2863 | int i, blocks = ocfs2_clusters_to_blocks(sb, new_len); | ||
2864 | u64 old_block = ocfs2_clusters_to_blocks(sb, old_cluster); | ||
2865 | u64 new_block = ocfs2_clusters_to_blocks(sb, new_cluster); | ||
2866 | struct ocfs2_super *osb = OCFS2_SB(sb); | ||
2867 | struct buffer_head *old_bh = NULL; | ||
2868 | struct buffer_head *new_bh = NULL; | ||
2869 | |||
2870 | mlog(0, "old_cluster %u, new %u, len %u\n", old_cluster, | ||
2871 | new_cluster, new_len); | ||
2872 | |||
2873 | for (i = 0; i < blocks; i++, old_block++, new_block++) { | ||
2874 | new_bh = sb_getblk(osb->sb, new_block); | ||
2875 | if (new_bh == NULL) { | ||
2876 | ret = -EIO; | ||
2877 | mlog_errno(ret); | ||
2878 | break; | ||
2879 | } | ||
2880 | |||
2881 | ocfs2_set_new_buffer_uptodate(ci, new_bh); | ||
2882 | |||
2883 | ret = ocfs2_read_block(ci, old_block, &old_bh, NULL); | ||
2884 | if (ret) { | ||
2885 | mlog_errno(ret); | ||
2886 | break; | ||
2887 | } | ||
2888 | |||
2889 | ret = ocfs2_journal_access(handle, ci, new_bh, | ||
2890 | OCFS2_JOURNAL_ACCESS_CREATE); | ||
2891 | if (ret) { | ||
2892 | mlog_errno(ret); | ||
2893 | break; | ||
2894 | } | ||
2895 | |||
2896 | memcpy(new_bh->b_data, old_bh->b_data, sb->s_blocksize); | ||
2897 | ret = ocfs2_journal_dirty(handle, new_bh); | ||
2898 | if (ret) { | ||
2899 | mlog_errno(ret); | ||
2900 | break; | ||
2901 | } | ||
2902 | |||
2903 | brelse(new_bh); | ||
2904 | brelse(old_bh); | ||
2905 | new_bh = NULL; | ||
2906 | old_bh = NULL; | ||
2907 | } | ||
2908 | |||
2909 | brelse(new_bh); | ||
2910 | brelse(old_bh); | ||
2911 | return ret; | ||
2912 | } | ||
2913 | |||
2851 | static int ocfs2_clear_ext_refcount(handle_t *handle, | 2914 | static int ocfs2_clear_ext_refcount(handle_t *handle, |
2852 | struct ocfs2_extent_tree *et, | 2915 | struct ocfs2_extent_tree *et, |
2853 | u32 cpos, u32 p_cluster, u32 len, | 2916 | u32 cpos, u32 p_cluster, u32 len, |
@@ -3026,6 +3089,10 @@ static int ocfs2_make_clusters_writable(struct super_block *sb, | |||
3026 | return ret; | 3089 | return ret; |
3027 | } | 3090 | } |
3028 | 3091 | ||
3092 | if (context->post_refcount) | ||
3093 | credits += context->post_refcount->credits; | ||
3094 | |||
3095 | credits += context->extra_credits; | ||
3029 | handle = ocfs2_start_trans(osb, credits); | 3096 | handle = ocfs2_start_trans(osb, credits); |
3030 | if (IS_ERR(handle)) { | 3097 | if (IS_ERR(handle)) { |
3031 | ret = PTR_ERR(handle); | 3098 | ret = PTR_ERR(handle); |
@@ -3105,13 +3172,25 @@ static int ocfs2_make_clusters_writable(struct super_block *sb, | |||
3105 | ref_leaf_bh = NULL; | 3172 | ref_leaf_bh = NULL; |
3106 | } | 3173 | } |
3107 | 3174 | ||
3175 | /* handle any post_cow action. */ | ||
3176 | if (context->post_refcount && context->post_refcount->func) { | ||
3177 | ret = context->post_refcount->func(context->inode, handle, | ||
3178 | context->post_refcount->para); | ||
3179 | if (ret) { | ||
3180 | mlog_errno(ret); | ||
3181 | goto out_commit; | ||
3182 | } | ||
3183 | } | ||
3184 | |||
3108 | /* | 3185 | /* |
3109 | * Here we should write the new page out first if we are | 3186 | * Here we should write the new page out first if we are |
3110 | * in write-back mode. | 3187 | * in write-back mode. |
3111 | */ | 3188 | */ |
3112 | ret = ocfs2_cow_sync_writeback(sb, context, cpos, num_clusters); | 3189 | if (context->get_clusters == ocfs2_di_get_clusters) { |
3113 | if (ret) | 3190 | ret = ocfs2_cow_sync_writeback(sb, context, cpos, num_clusters); |
3114 | mlog_errno(ret); | 3191 | if (ret) |
3192 | mlog_errno(ret); | ||
3193 | } | ||
3115 | 3194 | ||
3116 | out_commit: | 3195 | out_commit: |
3117 | ocfs2_commit_trans(osb, handle); | 3196 | ocfs2_commit_trans(osb, handle); |
@@ -3298,6 +3377,167 @@ int ocfs2_refcount_cow(struct inode *inode, | |||
3298 | return ret; | 3377 | return ret; |
3299 | } | 3378 | } |
3300 | 3379 | ||
3380 | static int ocfs2_xattr_value_get_clusters(struct ocfs2_cow_context *context, | ||
3381 | u32 v_cluster, u32 *p_cluster, | ||
3382 | u32 *num_clusters, | ||
3383 | unsigned int *extent_flags) | ||
3384 | { | ||
3385 | struct inode *inode = context->inode; | ||
3386 | struct ocfs2_xattr_value_root *xv = context->cow_object; | ||
3387 | |||
3388 | return ocfs2_xattr_get_clusters(inode, v_cluster, p_cluster, | ||
3389 | num_clusters, &xv->xr_list, | ||
3390 | extent_flags); | ||
3391 | } | ||
3392 | |||
3393 | /* | ||
3394 | * Given a xattr value root, calculate the most meta/credits we need for | ||
3395 | * refcount tree change if we truncate it to 0. | ||
3396 | */ | ||
3397 | int ocfs2_refcounted_xattr_delete_need(struct inode *inode, | ||
3398 | struct ocfs2_caching_info *ref_ci, | ||
3399 | struct buffer_head *ref_root_bh, | ||
3400 | struct ocfs2_xattr_value_root *xv, | ||
3401 | int *meta_add, int *credits) | ||
3402 | { | ||
3403 | int ret = 0, index, ref_blocks = 0; | ||
3404 | u32 p_cluster, num_clusters; | ||
3405 | u32 cpos = 0, clusters = le32_to_cpu(xv->xr_clusters); | ||
3406 | struct ocfs2_refcount_block *rb; | ||
3407 | struct ocfs2_refcount_rec rec; | ||
3408 | struct buffer_head *ref_leaf_bh = NULL; | ||
3409 | |||
3410 | while (cpos < clusters) { | ||
3411 | ret = ocfs2_xattr_get_clusters(inode, cpos, &p_cluster, | ||
3412 | &num_clusters, &xv->xr_list, | ||
3413 | NULL); | ||
3414 | if (ret) { | ||
3415 | mlog_errno(ret); | ||
3416 | goto out; | ||
3417 | } | ||
3418 | |||
3419 | cpos += num_clusters; | ||
3420 | |||
3421 | while (num_clusters) { | ||
3422 | ret = ocfs2_get_refcount_rec(ref_ci, ref_root_bh, | ||
3423 | p_cluster, num_clusters, | ||
3424 | &rec, &index, | ||
3425 | &ref_leaf_bh); | ||
3426 | if (ret) { | ||
3427 | mlog_errno(ret); | ||
3428 | goto out; | ||
3429 | } | ||
3430 | |||
3431 | BUG_ON(!rec.r_refcount); | ||
3432 | |||
3433 | rb = (struct ocfs2_refcount_block *)ref_leaf_bh->b_data; | ||
3434 | |||
3435 | /* | ||
3436 | * We really don't know whether the other clusters is in | ||
3437 | * this refcount block or not, so just take the worst | ||
3438 | * case that all the clusters are in this block and each | ||
3439 | * one will split a refcount rec, so totally we need | ||
3440 | * clusters * 2 new refcount rec. | ||
3441 | */ | ||
3442 | if (le64_to_cpu(rb->rf_records.rl_used) + clusters * 2 > | ||
3443 | le16_to_cpu(rb->rf_records.rl_count)) | ||
3444 | ref_blocks++; | ||
3445 | |||
3446 | *credits += 1; | ||
3447 | brelse(ref_leaf_bh); | ||
3448 | ref_leaf_bh = NULL; | ||
3449 | |||
3450 | if (num_clusters <= le32_to_cpu(rec.r_clusters)) | ||
3451 | break; | ||
3452 | else | ||
3453 | num_clusters -= le32_to_cpu(rec.r_clusters); | ||
3454 | p_cluster += num_clusters; | ||
3455 | } | ||
3456 | } | ||
3457 | |||
3458 | *meta_add += ref_blocks; | ||
3459 | if (!ref_blocks) | ||
3460 | goto out; | ||
3461 | |||
3462 | rb = (struct ocfs2_refcount_block *)ref_root_bh->b_data; | ||
3463 | if (le32_to_cpu(rb->rf_flags) & OCFS2_REFCOUNT_TREE_FL) | ||
3464 | *credits += OCFS2_EXPAND_REFCOUNT_TREE_CREDITS; | ||
3465 | else { | ||
3466 | struct ocfs2_extent_tree et; | ||
3467 | |||
3468 | ocfs2_init_refcount_extent_tree(&et, ref_ci, ref_root_bh); | ||
3469 | *credits += ocfs2_calc_extend_credits(inode->i_sb, | ||
3470 | et.et_root_el, | ||
3471 | ref_blocks); | ||
3472 | } | ||
3473 | |||
3474 | out: | ||
3475 | brelse(ref_leaf_bh); | ||
3476 | return ret; | ||
3477 | } | ||
3478 | |||
3479 | /* | ||
3480 | * Do CoW for xattr. | ||
3481 | */ | ||
3482 | int ocfs2_refcount_cow_xattr(struct inode *inode, | ||
3483 | struct ocfs2_dinode *di, | ||
3484 | struct ocfs2_xattr_value_buf *vb, | ||
3485 | struct ocfs2_refcount_tree *ref_tree, | ||
3486 | struct buffer_head *ref_root_bh, | ||
3487 | u32 cpos, u32 write_len, | ||
3488 | struct ocfs2_post_refcount *post) | ||
3489 | { | ||
3490 | int ret; | ||
3491 | struct ocfs2_xattr_value_root *xv = vb->vb_xv; | ||
3492 | struct ocfs2_inode_info *oi = OCFS2_I(inode); | ||
3493 | struct ocfs2_cow_context *context = NULL; | ||
3494 | u32 cow_start, cow_len; | ||
3495 | |||
3496 | BUG_ON(!(oi->ip_dyn_features & OCFS2_HAS_REFCOUNT_FL)); | ||
3497 | |||
3498 | ret = ocfs2_refcount_cal_cow_clusters(inode, &xv->xr_list, | ||
3499 | cpos, write_len, UINT_MAX, | ||
3500 | &cow_start, &cow_len); | ||
3501 | if (ret) { | ||
3502 | mlog_errno(ret); | ||
3503 | goto out; | ||
3504 | } | ||
3505 | |||
3506 | BUG_ON(cow_len == 0); | ||
3507 | |||
3508 | context = kzalloc(sizeof(struct ocfs2_cow_context), GFP_NOFS); | ||
3509 | if (!context) { | ||
3510 | ret = -ENOMEM; | ||
3511 | mlog_errno(ret); | ||
3512 | goto out; | ||
3513 | } | ||
3514 | |||
3515 | context->inode = inode; | ||
3516 | context->cow_start = cow_start; | ||
3517 | context->cow_len = cow_len; | ||
3518 | context->ref_tree = ref_tree; | ||
3519 | context->ref_root_bh = ref_root_bh;; | ||
3520 | context->cow_object = xv; | ||
3521 | |||
3522 | context->cow_duplicate_clusters = ocfs2_duplicate_clusters_by_jbd; | ||
3523 | /* We need the extra credits for duplicate_clusters by jbd. */ | ||
3524 | context->extra_credits = | ||
3525 | ocfs2_clusters_to_blocks(inode->i_sb, 1) * cow_len; | ||
3526 | context->get_clusters = ocfs2_xattr_value_get_clusters; | ||
3527 | context->post_refcount = post; | ||
3528 | |||
3529 | ocfs2_init_xattr_value_extent_tree(&context->data_et, | ||
3530 | INODE_CACHE(inode), vb); | ||
3531 | |||
3532 | ret = ocfs2_replace_cow(context); | ||
3533 | if (ret) | ||
3534 | mlog_errno(ret); | ||
3535 | |||
3536 | out: | ||
3537 | kfree(context); | ||
3538 | return ret; | ||
3539 | } | ||
3540 | |||
3301 | /* | 3541 | /* |
3302 | * Insert a new extent into refcount tree and mark a extent rec | 3542 | * Insert a new extent into refcount tree and mark a extent rec |
3303 | * as refcounted in the dinode tree. | 3543 | * as refcounted in the dinode tree. |