ocfs2: CoW a reflinked cluster when it is truncated.
authorTao Ma <tao.ma@oracle.com>
Wed, 26 Aug 2009 01:47:28 +0000 (09:47 +0800)
committerJoel Becker <joel.becker@oracle.com>
Wed, 23 Sep 2009 03:09:38 +0000 (20:09 -0700)
When we truncate a file to a specific size which resides in a reflinked
cluster, we need to CoW it since ocfs2_zero_range_for_truncate will
zero the space after the size(just another type of write).

So we add a "max_cpos" in ocfs2_refcount_cow so that it will stop when
it hit the max cluster offset.

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

index 9db9d64..33e03c5 100644 (file)
@@ -1712,7 +1712,7 @@ int ocfs2_write_begin_nolock(struct address_space *mapping,
                goto out;
        } else if (ret == 1) {
                ret = ocfs2_refcount_cow(inode, di_bh,
-                                        wc->w_cpos, wc->w_clen);
+                                        wc->w_cpos, wc->w_clen, UINT_MAX);
                if (ret) {
                        mlog_errno(ret);
                        goto out;
index 6ee20e8..75f5b81 100644 (file)
@@ -335,6 +335,39 @@ out:
        return ret;
 }
 
+static int ocfs2_cow_file_pos(struct inode *inode,
+                             struct buffer_head *fe_bh,
+                             u64 offset)
+{
+       int status;
+       u32 phys, cpos = offset >> OCFS2_SB(inode->i_sb)->s_clustersize_bits;
+       unsigned int num_clusters = 0;
+       unsigned int ext_flags = 0;
+
+       /*
+        * If the new offset is aligned to the range of the cluster, there is
+        * no space for ocfs2_zero_range_for_truncate to fill, so no need to
+        * CoW either.
+        */
+       if ((offset & (OCFS2_SB(inode->i_sb)->s_clustersize - 1)) == 0)
+               return 0;
+
+       status = ocfs2_get_clusters(inode, cpos, &phys,
+                                   &num_clusters, &ext_flags);
+       if (status) {
+               mlog_errno(status);
+               goto out;
+       }
+
+       if (!(ext_flags & OCFS2_EXT_REFCOUNTED))
+               goto out;
+
+       return ocfs2_refcount_cow(inode, fe_bh, cpos, 1, cpos+1);
+
+out:
+       return status;
+}
+
 static int ocfs2_orphan_for_truncate(struct ocfs2_super *osb,
                                     struct inode *inode,
                                     struct buffer_head *fe_bh,
@@ -347,6 +380,17 @@ static int ocfs2_orphan_for_truncate(struct ocfs2_super *osb,
 
        mlog_entry_void();
 
+       /*
+        * We need to CoW the cluster contains the offset if it is reflinked
+        * since we will call ocfs2_zero_range_for_truncate later which will
+        * write "0" from offset to the end of the cluster.
+        */
+       status = ocfs2_cow_file_pos(inode, fe_bh, new_i_size);
+       if (status) {
+               mlog_errno(status);
+               return status;
+       }
+
        /* TODO: This needs to actually orphan the inode in this
         * transaction. */
 
@@ -1713,7 +1757,7 @@ static int ocfs2_prepare_inode_for_refcount(struct inode *inode,
 
        *meta_level = 1;
 
-       ret = ocfs2_refcount_cow(inode, di_bh, cpos, clusters);
+       ret = ocfs2_refcount_cow(inode, di_bh, cpos, clusters, UINT_MAX);
        if (ret)
                mlog_errno(ret);
 out:
index 0a92436..37aa0c8 100644 (file)
@@ -2482,6 +2482,7 @@ static inline unsigned int ocfs2_cow_align_length(struct super_block *sb,
  *
  * cpos is vitual start cluster position we want to do CoW in a
  * file and write_len is the cluster length.
+ * max_cpos is the place where we want to stop CoW intentionally.
  *
  * Normal we will start CoW from the beginning of extent record cotaining cpos.
  * We try to break up extents on boundaries of MAX_CONTIG_BYTES so that we
@@ -2491,6 +2492,7 @@ static int ocfs2_refcount_cal_cow_clusters(struct inode *inode,
                                           struct buffer_head *di_bh,
                                           u32 cpos,
                                           u32 write_len,
+                                          u32 max_cpos,
                                           u32 *cow_start,
                                           u32 *cow_len)
 {
@@ -2505,6 +2507,8 @@ static int ocfs2_refcount_cal_cow_clusters(struct inode *inode,
        int contig_clusters = ocfs2_cow_contig_clusters(inode->i_sb);
        int leaf_clusters;
 
+       BUG_ON(cpos + write_len > max_cpos);
+
        if (tree_height > 0) {
                ret = ocfs2_find_leaf(INODE_CACHE(inode), el, cpos, &eb_bh);
                if (ret) {
@@ -2549,15 +2553,20 @@ static int ocfs2_refcount_cal_cow_clusters(struct inode *inode,
                }
 
                /*
-                * If we encounter a hole or a non-refcounted record,
-                * stop the search.
+                * If we encounter a hole, a non-refcounted record or
+                * pass the max_cpos, stop the search.
                 */
                if ((!(rec->e_flags & OCFS2_EXT_REFCOUNTED)) ||
-                   (*cow_len && rec_end != le32_to_cpu(rec->e_cpos)))
+                   (*cow_len && rec_end != le32_to_cpu(rec->e_cpos)) ||
+                   (max_cpos <= le32_to_cpu(rec->e_cpos)))
                        break;
 
                leaf_clusters = le16_to_cpu(rec->e_leaf_clusters);
                rec_end = le32_to_cpu(rec->e_cpos) + leaf_clusters;
+               if (rec_end > max_cpos) {
+                       rec_end = max_cpos;
+                       leaf_clusters = rec_end - le32_to_cpu(rec->e_cpos);
+               }
 
                /*
                 * How many clusters do we actually need from
@@ -3184,12 +3193,13 @@ static int ocfs2_replace_cow(struct inode *inode,
 }
 
 /*
- * Starting at cpos, try to CoW write_len clusters.
- * This will stop when it runs into a hole or an unrefcounted extent.
+ * Starting at cpos, try to CoW write_len clusters.  Don't CoW
+ * past max_cpos.  This will stop when it runs into a hole or an
+ * unrefcounted extent.
  */
 static int ocfs2_refcount_cow_hunk(struct inode *inode,
                                   struct buffer_head *di_bh,
-                                  u32 cpos, u32 write_len)
+                                  u32 cpos, u32 write_len, u32 max_cpos)
 {
        int ret;
        u32 cow_start = 0, cow_len = 0;
@@ -3201,12 +3211,14 @@ static int ocfs2_refcount_cow_hunk(struct inode *inode,
 
        BUG_ON(!(oi->ip_dyn_features & OCFS2_HAS_REFCOUNT_FL));
 
-       ret = ocfs2_refcount_cal_cow_clusters(inode, di_bh, cpos, write_len,
+       ret = ocfs2_refcount_cal_cow_clusters(inode, di_bh,
+                                             cpos, write_len, max_cpos,
                                              &cow_start, &cow_len);
        if (ret) {
                mlog_errno(ret);
                goto out;
        }
+
        mlog(0, "CoW inode %lu, cpos %u, write_len %u, cow_start %u, "
             "cow_len %u\n", inode->i_ino,
             cpos, write_len, cow_start, cow_len);
@@ -3233,12 +3245,12 @@ out:
 
 /*
  * CoW any and all clusters between cpos and cpos+write_len.
- * If this returns successfully, all clusters between cpos and
- * cpos+write_len are safe to modify.
+ * Don't CoW past max_cpos.  If this returns successfully, all
+ * clusters between cpos and cpos+write_len are safe to modify.
  */
 int ocfs2_refcount_cow(struct inode *inode,
                       struct buffer_head *di_bh,
-                      u32 cpos, u32 write_len)
+                      u32 cpos, u32 write_len, u32 max_cpos)
 {
        int ret = 0;
        u32 p_cluster, num_clusters;
@@ -3257,7 +3269,7 @@ int ocfs2_refcount_cow(struct inode *inode,
 
                if (ext_flags & OCFS2_EXT_REFCOUNTED) {
                        ret = ocfs2_refcount_cow_hunk(inode, di_bh, cpos,
-                                                     num_clusters);
+                                                     num_clusters, max_cpos);
                        if (ret) {
                                mlog_errno(ret);
                                break;
index a8c15b0..356f99c 100644 (file)
@@ -53,5 +53,5 @@ int ocfs2_prepare_refcount_change_for_del(struct inode *inode,
                                          int *credits,
                                          struct ocfs2_alloc_context **meta_ac);
 int ocfs2_refcount_cow(struct inode *inode, struct buffer_head *di_bh,
-                      u32 cpos, u32 write_len);
+                      u32 cpos, u32 write_len, u32 max_cpos);
 #endif /* OCFS2_REFCOUNTTREE_H */