]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - fs/ocfs2/refcounttree.c
ocfs2: CoW refcount tree improvement.
[karo-tx-linux.git] / fs / ocfs2 / refcounttree.c
index 4e7df8b8fd4f7fbc9c907ed77334c41016d1b8eb..0a92436557e34cc13d309c1a44cd19061f0f8c21 100644 (file)
@@ -2076,7 +2076,8 @@ static int __ocfs2_decrease_refcount(handle_t *handle,
                                     struct buffer_head *ref_root_bh,
                                     u64 cpos, u32 len,
                                     struct ocfs2_alloc_context *meta_ac,
-                                    struct ocfs2_cached_dealloc_ctxt *dealloc)
+                                    struct ocfs2_cached_dealloc_ctxt *dealloc,
+                                    int delete)
 {
        int ret = 0, index = 0;
        struct ocfs2_refcount_rec rec;
@@ -2084,9 +2085,10 @@ static int __ocfs2_decrease_refcount(handle_t *handle,
        struct super_block *sb = ocfs2_metadata_cache_get_super(ci);
        struct buffer_head *ref_leaf_bh = NULL;
 
-       mlog(0, "Tree owner %llu, decrease refcount start %llu, len %u\n",
+       mlog(0, "Tree owner %llu, decrease refcount start %llu, "
+            "len %u, delete %u\n",
             (unsigned long long)ocfs2_metadata_cache_owner(ci),
-            (unsigned long long)cpos, len);
+            (unsigned long long)cpos, len, delete);
 
        while (len) {
                ret = ocfs2_get_refcount_rec(ci, ref_root_bh,
@@ -2099,6 +2101,8 @@ static int __ocfs2_decrease_refcount(handle_t *handle,
 
                r_count = le32_to_cpu(rec.r_refcount);
                BUG_ON(r_count == 0);
+               if (!delete)
+                       BUG_ON(r_count > 1);
 
                r_len = min((u64)(cpos + len), le64_to_cpu(rec.r_cpos) +
                              le32_to_cpu(rec.r_clusters)) - cpos;
@@ -2112,7 +2116,7 @@ static int __ocfs2_decrease_refcount(handle_t *handle,
                        goto out;
                }
 
-               if (le32_to_cpu(rec.r_refcount) == 1) {
+               if (le32_to_cpu(rec.r_refcount) == 1 && delete) {
                        ret = ocfs2_cache_cluster_dealloc(dealloc,
                                          ocfs2_clusters_to_blocks(sb, cpos),
                                                          r_len);
@@ -2137,7 +2141,8 @@ out:
 int ocfs2_decrease_refcount(struct inode *inode,
                            handle_t *handle, u32 cpos, u32 len,
                            struct ocfs2_alloc_context *meta_ac,
-                           struct ocfs2_cached_dealloc_ctxt *dealloc)
+                           struct ocfs2_cached_dealloc_ctxt *dealloc,
+                           int delete)
 {
        int ret;
        u64 ref_blkno;
@@ -2167,7 +2172,7 @@ int ocfs2_decrease_refcount(struct inode *inode,
        }
 
        ret = __ocfs2_decrease_refcount(handle, &tree->rf_ci, ref_root_bh,
-                                       cpos, len, meta_ac, dealloc);
+                                       cpos, len, meta_ac, dealloc, delete);
        if (ret)
                mlog_errno(ret);
 out:
@@ -2974,10 +2979,16 @@ static int ocfs2_make_clusters_writable(struct super_block *sb,
                                        u32 cpos, u32 p_cluster,
                                        u32 num_clusters, unsigned int e_flags)
 {
-       int ret, credits =  0;
+       int ret, delete, index, credits =  0;
        u32 new_bit, new_len;
+       unsigned int set_len;
        struct ocfs2_super *osb = OCFS2_SB(sb);
        handle_t *handle;
+       struct buffer_head *ref_leaf_bh = NULL;
+       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,
@@ -2998,35 +3009,75 @@ static int ocfs2_make_clusters_writable(struct super_block *sb,
        }
 
        while (num_clusters) {
-               ret = __ocfs2_claim_clusters(osb, handle, context->data_ac,
-                                            1, num_clusters,
-                                            &new_bit, &new_len);
+               ret = ocfs2_get_refcount_rec(context->ref_ci,
+                                            context->ref_root_bh,
+                                            p_cluster, num_clusters,
+                                            &rec, &index, &ref_leaf_bh);
                if (ret) {
                        mlog_errno(ret);
                        goto out_commit;
                }
 
-               ret = ocfs2_replace_clusters(handle, context,
-                                            cpos, p_cluster, new_bit,
-                                            new_len, e_flags);
+               BUG_ON(!rec.r_refcount);
+               set_len = min((u64)p_cluster + num_clusters,
+                             le64_to_cpu(rec.r_cpos) +
+                             le32_to_cpu(rec.r_clusters)) - p_cluster;
+
+               /*
+                * There are many different situation here.
+                * 1. If refcount == 1, remove the flag and don't COW.
+                * 2. If refcount > 1, allocate clusters.
+                *    Here we may not allocate r_len once at a time, so continue
+                *    until we reach num_clusters.
+                */
+               if (le32_to_cpu(rec.r_refcount) == 1) {
+                       delete = 0;
+                       ret = ocfs2_clear_ext_refcount(handle, &context->di_et,
+                                                      cpos, p_cluster,
+                                                      set_len, e_flags,
+                                                      context->meta_ac,
+                                                      &context->dealloc);
+                       if (ret) {
+                               mlog_errno(ret);
+                               goto out_commit;
+                       }
+               } else {
+                       delete = 1;
+
+                       ret = __ocfs2_claim_clusters(osb, handle,
+                                                    context->data_ac,
+                                                    1, set_len,
+                                                    &new_bit, &new_len);
+                       if (ret) {
+                               mlog_errno(ret);
+                               goto out_commit;
+                       }
+
+                       ret = ocfs2_replace_clusters(handle, context,
+                                                    cpos, p_cluster, new_bit,
+                                                    new_len, e_flags);
+                       if (ret) {
+                               mlog_errno(ret);
+                               goto out_commit;
+                       }
+                       set_len = new_len;
+               }
+
+               ret = __ocfs2_decrease_refcount(handle, context->ref_ci,
+                                               context->ref_root_bh,
+                                               p_cluster, set_len,
+                                               context->meta_ac,
+                                               &context->dealloc, delete);
                if (ret) {
                        mlog_errno(ret);
                        goto out_commit;
                }
 
-               cpos += new_len;
-               p_cluster += new_len;
-               num_clusters -= new_len;
-       }
-
-       ret = __ocfs2_decrease_refcount(handle, context->ref_ci,
-                                       context->ref_root_bh,
-                                       p_cluster, num_clusters,
-                                       context->meta_ac,
-                                       &context->dealloc);
-       if (ret) {
-               mlog_errno(ret);
-               goto out_commit;
+               cpos += set_len;
+               p_cluster += set_len;
+               num_clusters -= set_len;
+               brelse(ref_leaf_bh);
+               ref_leaf_bh = NULL;
        }
 
        /*
@@ -3049,6 +3100,7 @@ out:
                ocfs2_free_alloc_context(context->meta_ac);
                context->meta_ac = NULL;
        }
+       brelse(ref_leaf_bh);
 
        return ret;
 }