ocfs2: Abstract duplicate clusters process in CoW.
authorTao Ma <tao.ma@oracle.com>
Mon, 24 Aug 2009 06:31:03 +0000 (14:31 +0800)
committerJoel Becker <joel.becker@oracle.com>
Wed, 23 Sep 2009 03:09:40 +0000 (20:09 -0700)
We currently use pagecache to duplicate clusters in CoW,
but it isn't suitable for xattr case. So abstract it out
so that the caller can decide which method it use.

Signed-off-by: Tao Ma <tao.ma@oracle.com>
fs/ocfs2/refcounttree.c

index 62d21c6..40de7bb 100644 (file)
@@ -45,12 +45,20 @@ struct ocfs2_cow_context {
        struct inode *inode;
        u32 cow_start;
        u32 cow_len;
-       struct ocfs2_extent_tree di_et;
-       struct ocfs2_caching_info *ref_ci;
+       struct ocfs2_extent_tree data_et;
+       struct ocfs2_refcount_tree *ref_tree;
        struct buffer_head *ref_root_bh;
        struct ocfs2_alloc_context *meta_ac;
        struct ocfs2_alloc_context *data_ac;
        struct ocfs2_cached_dealloc_ctxt dealloc;
+       int (*get_clusters)(struct ocfs2_cow_context *context,
+                           u32 v_cluster, u32 *p_cluster,
+                           u32 *num_clusters,
+                           unsigned int *extent_flags);
+       int (*cow_duplicate_clusters)(handle_t *handle,
+                                     struct ocfs2_cow_context *context,
+                                     u32 cpos, u32 old_cluster,
+                                     u32 new_cluster, u32 new_len);
 };
 
 static inline struct ocfs2_refcount_tree *
@@ -2489,7 +2497,7 @@ static inline unsigned int ocfs2_cow_align_length(struct super_block *sb,
  * get good I/O from the resulting extent tree.
  */
 static int ocfs2_refcount_cal_cow_clusters(struct inode *inode,
-                                          struct buffer_head *di_bh,
+                                          struct ocfs2_extent_list *el,
                                           u32 cpos,
                                           u32 write_len,
                                           u32 max_cpos,
@@ -2497,8 +2505,6 @@ static int ocfs2_refcount_cal_cow_clusters(struct inode *inode,
                                           u32 *cow_len)
 {
        int ret = 0;
-       struct ocfs2_dinode *di = (struct ocfs2_dinode *) di_bh->b_data;
-       struct ocfs2_extent_list *el = &di->id2.i_list;
        int tree_height = le16_to_cpu(el->l_tree_depth), i;
        struct buffer_head *eb_bh = NULL;
        struct ocfs2_extent_block *eb = NULL;
@@ -2769,13 +2775,13 @@ static int ocfs2_clear_cow_buffer(handle_t *handle, struct buffer_head *bh)
        return 0;
 }
 
-static int ocfs2_duplicate_clusters(handle_t *handle,
-                                   struct ocfs2_cow_context *context,
-                                   u32 cpos, u32 old_cluster,
-                                   u32 new_cluster, u32 new_len)
+static int ocfs2_duplicate_clusters_by_page(handle_t *handle,
+                                           struct ocfs2_cow_context *context,
+                                           u32 cpos, u32 old_cluster,
+                                           u32 new_cluster, u32 new_len)
 {
        int ret = 0, partial;
-       struct ocfs2_caching_info *ci = context->di_et.et_ci;
+       struct ocfs2_caching_info *ci = context->data_et.et_ci;
        struct super_block *sb = ocfs2_metadata_cache_get_super(ci);
        u64 new_block = ocfs2_clusters_to_blocks(sb, new_cluster);
        struct page *page;
@@ -2909,7 +2915,7 @@ static int ocfs2_replace_clusters(handle_t *handle,
                                  unsigned int ext_flags)
 {
        int ret;
-       struct ocfs2_caching_info *ci = context->di_et.et_ci;
+       struct ocfs2_caching_info *ci = context->data_et.et_ci;
        u64 ino = ocfs2_metadata_cache_owner(ci);
 
        mlog(0, "inode %llu, cpos %u, old %u, new %u, len %u, ext_flags %u\n",
@@ -2917,15 +2923,15 @@ static int ocfs2_replace_clusters(handle_t *handle,
 
        /*If the old clusters is unwritten, no need to duplicate. */
        if (!(ext_flags & OCFS2_EXT_UNWRITTEN)) {
-               ret = ocfs2_duplicate_clusters(handle, context, cpos,
-                                              old, new, len);
+               ret = context->cow_duplicate_clusters(handle, context, cpos,
+                                                     old, new, len);
                if (ret) {
                        mlog_errno(ret);
                        goto out;
                }
        }
 
-       ret = ocfs2_clear_ext_refcount(handle, &context->di_et,
+       ret = ocfs2_clear_ext_refcount(handle, &context->data_et,
                                       cpos, new, len, ext_flags,
                                       context->meta_ac, &context->dealloc);
        if (ret)
@@ -2983,6 +2989,15 @@ static int ocfs2_cow_sync_writeback(struct super_block *sb,
        return ret;
 }
 
+static int ocfs2_di_get_clusters(struct ocfs2_cow_context *context,
+                                u32 v_cluster, u32 *p_cluster,
+                                u32 *num_clusters,
+                                unsigned int *extent_flags)
+{
+       return ocfs2_get_clusters(context->inode, v_cluster, p_cluster,
+                                 num_clusters, extent_flags);
+}
+
 static int ocfs2_make_clusters_writable(struct super_block *sb,
                                        struct ocfs2_cow_context *context,
                                        u32 cpos, u32 p_cluster,
@@ -2994,14 +3009,15 @@ static int ocfs2_make_clusters_writable(struct super_block *sb,
        struct ocfs2_super *osb = OCFS2_SB(sb);
        handle_t *handle;
        struct buffer_head *ref_leaf_bh = NULL;
+       struct ocfs2_caching_info *ref_ci = &context->ref_tree->rf_ci;
        struct ocfs2_refcount_rec rec;
 
        mlog(0, "cpos %u, p_cluster %u, num_clusters %u, e_flags %u\n",
             cpos, p_cluster, num_clusters, e_flags);
 
        ret = ocfs2_lock_refcount_allocators(sb, p_cluster, num_clusters,
-                                            &context->di_et,
-                                            context->ref_ci,
+                                            &context->data_et,
+                                            ref_ci,
                                             context->ref_root_bh,
                                             &context->meta_ac,
                                             &context->data_ac, &credits);
@@ -3018,8 +3034,7 @@ static int ocfs2_make_clusters_writable(struct super_block *sb,
        }
 
        while (num_clusters) {
-               ret = ocfs2_get_refcount_rec(context->ref_ci,
-                                            context->ref_root_bh,
+               ret = ocfs2_get_refcount_rec(ref_ci, context->ref_root_bh,
                                             p_cluster, num_clusters,
                                             &rec, &index, &ref_leaf_bh);
                if (ret) {
@@ -3041,7 +3056,8 @@ static int ocfs2_make_clusters_writable(struct super_block *sb,
                 */
                if (le32_to_cpu(rec.r_refcount) == 1) {
                        delete = 0;
-                       ret = ocfs2_clear_ext_refcount(handle, &context->di_et,
+                       ret = ocfs2_clear_ext_refcount(handle,
+                                                      &context->data_et,
                                                       cpos, p_cluster,
                                                       set_len, e_flags,
                                                       context->meta_ac,
@@ -3072,7 +3088,7 @@ static int ocfs2_make_clusters_writable(struct super_block *sb,
                        set_len = new_len;
                }
 
-               ret = __ocfs2_decrease_refcount(handle, context->ref_ci,
+               ret = __ocfs2_decrease_refcount(handle, ref_ci,
                                                context->ref_root_bh,
                                                p_cluster, set_len,
                                                context->meta_ac,
@@ -3114,17 +3130,14 @@ out:
        return ret;
 }
 
-static int ocfs2_replace_cow(struct inode *inode,
-                            struct buffer_head *di_bh,
-                            struct buffer_head *ref_root_bh,
-                            struct ocfs2_caching_info *ref_ci,
-                            u32 cow_start, u32 cow_len)
+static int ocfs2_replace_cow(struct ocfs2_cow_context *context)
 {
        int ret = 0;
-       u32 p_cluster, num_clusters, start = cow_start;
+       struct inode *inode = context->inode;
+       u32 cow_start = context->cow_start, cow_len = context->cow_len;
+       u32 p_cluster, num_clusters;
        unsigned int ext_flags;
        struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
-       struct ocfs2_cow_context *context;
 
        if (!ocfs2_refcount_tree(OCFS2_SB(inode->i_sb))) {
                ocfs2_error(inode->i_sb, "Inode %lu want to use refcount "
@@ -3133,26 +3146,11 @@ static int ocfs2_replace_cow(struct inode *inode,
                return -EROFS;
        }
 
-       context = kzalloc(sizeof(struct ocfs2_cow_context), GFP_NOFS);
-       if (!context) {
-               ret = -ENOMEM;
-               mlog_errno(ret);
-               return ret;
-       }
-
-       context->inode = inode;
-       context->cow_start = cow_start;
-       context->cow_len = cow_len;
-       context->ref_ci = ref_ci;
-       context->ref_root_bh = ref_root_bh;
-
        ocfs2_init_dealloc_ctxt(&context->dealloc);
-       ocfs2_init_dinode_extent_tree(&context->di_et,
-                                     INODE_CACHE(inode), di_bh);
 
        while (cow_len) {
-               ret = ocfs2_get_clusters(inode, cow_start, &p_cluster,
-                                        &num_clusters, &ext_flags);
+               ret = context->get_clusters(context, cow_start, &p_cluster,
+                                           &num_clusters, &ext_flags);
                if (ret) {
                        mlog_errno(ret);
                        break;
@@ -3175,20 +3173,11 @@ static int ocfs2_replace_cow(struct inode *inode,
                cow_start += num_clusters;
        }
 
-
-       /*
-        * truncate the extent map here since no matter whether we meet with
-        * any error during the action, we shouldn't trust cached extent map
-        * any more.
-        */
-       ocfs2_extent_map_trunc(inode, start);
-
        if (ocfs2_dealloc_has_cluster(&context->dealloc)) {
                ocfs2_schedule_truncate_log_flush(osb, 1);
                ocfs2_run_deallocs(osb, &context->dealloc);
        }
 
-       kfree(context);
        return ret;
 }
 
@@ -3208,10 +3197,11 @@ static int ocfs2_refcount_cow_hunk(struct inode *inode,
        struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;
        struct buffer_head *ref_root_bh = NULL;
        struct ocfs2_refcount_tree *ref_tree;
+       struct ocfs2_cow_context *context = NULL;
 
        BUG_ON(!(oi->ip_dyn_features & OCFS2_HAS_REFCOUNT_FL));
 
-       ret = ocfs2_refcount_cal_cow_clusters(inode, di_bh,
+       ret = ocfs2_refcount_cal_cow_clusters(inode, &di->id2.i_list,
                                              cpos, write_len, max_cpos,
                                              &cow_start, &cow_len);
        if (ret) {
@@ -3225,6 +3215,13 @@ static int ocfs2_refcount_cow_hunk(struct inode *inode,
 
        BUG_ON(cow_len == 0);
 
+       context = kzalloc(sizeof(struct ocfs2_cow_context), GFP_NOFS);
+       if (!context) {
+               ret = -ENOMEM;
+               mlog_errno(ret);
+               goto out;
+       }
+
        ret = ocfs2_lock_refcount_tree(osb, le64_to_cpu(di->i_refcount_loc),
                                       1, &ref_tree, &ref_root_bh);
        if (ret) {
@@ -3232,14 +3229,32 @@ static int ocfs2_refcount_cow_hunk(struct inode *inode,
                goto out;
        }
 
-       ret = ocfs2_replace_cow(inode, di_bh, ref_root_bh, &ref_tree->rf_ci,
-                               cow_start, cow_len);
+       context->inode = inode;
+       context->cow_start = cow_start;
+       context->cow_len = cow_len;
+       context->ref_tree = ref_tree;
+       context->ref_root_bh = ref_root_bh;
+       context->cow_duplicate_clusters = ocfs2_duplicate_clusters_by_page;
+       context->get_clusters = ocfs2_di_get_clusters;
+
+       ocfs2_init_dinode_extent_tree(&context->data_et,
+                                     INODE_CACHE(inode), di_bh);
+
+       ret = ocfs2_replace_cow(context);
        if (ret)
                mlog_errno(ret);
 
+       /*
+        * truncate the extent map here since no matter whether we meet with
+        * any error during the action, we shouldn't trust cached extent map
+        * any more.
+        */
+       ocfs2_extent_map_trunc(inode, cow_start);
+
        ocfs2_unlock_refcount_tree(osb, ref_tree, 1);
        brelse(ref_root_bh);
 out:
+       kfree(context);
        return ret;
 }