From 37f8a2bfaa8364dd3644cccee8824bb8f5e409a5 Mon Sep 17 00:00:00 2001 From: Tao Ma Date: Wed, 26 Aug 2009 09:47:28 +0800 Subject: [PATCH] ocfs2: CoW a reflinked cluster when it is truncated. 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 --- fs/ocfs2/aops.c | 2 +- fs/ocfs2/file.c | 46 ++++++++++++++++++++++++++++++++++++++++- fs/ocfs2/refcounttree.c | 34 ++++++++++++++++++++---------- fs/ocfs2/refcounttree.h | 2 +- 4 files changed, 70 insertions(+), 14 deletions(-) diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index 9db9d64ca47..33e03c55112 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -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; diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 6ee20e82bcc..75f5b81805b 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -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: diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c index 0a92436557e..37aa0c8696d 100644 --- a/fs/ocfs2/refcounttree.c +++ b/fs/ocfs2/refcounttree.c @@ -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; diff --git a/fs/ocfs2/refcounttree.h b/fs/ocfs2/refcounttree.h index a8c15b0b230..356f99c8563 100644 --- a/fs/ocfs2/refcounttree.h +++ b/fs/ocfs2/refcounttree.h @@ -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 */