Commit 37f8a2bf authored by Tao Ma's avatar Tao Ma Committed by Joel Becker

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: default avatarTao Ma <tao.ma@oracle.com>
parent 293b2f70
...@@ -1712,7 +1712,7 @@ int ocfs2_write_begin_nolock(struct address_space *mapping, ...@@ -1712,7 +1712,7 @@ int ocfs2_write_begin_nolock(struct address_space *mapping,
goto out; goto out;
} else if (ret == 1) { } else if (ret == 1) {
ret = ocfs2_refcount_cow(inode, di_bh, ret = ocfs2_refcount_cow(inode, di_bh,
wc->w_cpos, wc->w_clen); wc->w_cpos, wc->w_clen, UINT_MAX);
if (ret) { if (ret) {
mlog_errno(ret); mlog_errno(ret);
goto out; goto out;
......
...@@ -335,6 +335,39 @@ out: ...@@ -335,6 +335,39 @@ out:
return ret; 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, static int ocfs2_orphan_for_truncate(struct ocfs2_super *osb,
struct inode *inode, struct inode *inode,
struct buffer_head *fe_bh, struct buffer_head *fe_bh,
...@@ -347,6 +380,17 @@ static int ocfs2_orphan_for_truncate(struct ocfs2_super *osb, ...@@ -347,6 +380,17 @@ static int ocfs2_orphan_for_truncate(struct ocfs2_super *osb,
mlog_entry_void(); 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 /* TODO: This needs to actually orphan the inode in this
* transaction. */ * transaction. */
...@@ -1713,7 +1757,7 @@ static int ocfs2_prepare_inode_for_refcount(struct inode *inode, ...@@ -1713,7 +1757,7 @@ static int ocfs2_prepare_inode_for_refcount(struct inode *inode,
*meta_level = 1; *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) if (ret)
mlog_errno(ret); mlog_errno(ret);
out: out:
......
...@@ -2482,6 +2482,7 @@ static inline unsigned int ocfs2_cow_align_length(struct super_block *sb, ...@@ -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 * cpos is vitual start cluster position we want to do CoW in a
* file and write_len is the cluster length. * 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. * 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 * 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, ...@@ -2491,6 +2492,7 @@ static int ocfs2_refcount_cal_cow_clusters(struct inode *inode,
struct buffer_head *di_bh, struct buffer_head *di_bh,
u32 cpos, u32 cpos,
u32 write_len, u32 write_len,
u32 max_cpos,
u32 *cow_start, u32 *cow_start,
u32 *cow_len) u32 *cow_len)
{ {
...@@ -2505,6 +2507,8 @@ static int ocfs2_refcount_cal_cow_clusters(struct inode *inode, ...@@ -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 contig_clusters = ocfs2_cow_contig_clusters(inode->i_sb);
int leaf_clusters; int leaf_clusters;
BUG_ON(cpos + write_len > max_cpos);
if (tree_height > 0) { if (tree_height > 0) {
ret = ocfs2_find_leaf(INODE_CACHE(inode), el, cpos, &eb_bh); ret = ocfs2_find_leaf(INODE_CACHE(inode), el, cpos, &eb_bh);
if (ret) { if (ret) {
...@@ -2549,15 +2553,20 @@ static int ocfs2_refcount_cal_cow_clusters(struct inode *inode, ...@@ -2549,15 +2553,20 @@ static int ocfs2_refcount_cal_cow_clusters(struct inode *inode,
} }
/* /*
* If we encounter a hole or a non-refcounted record, * If we encounter a hole, a non-refcounted record or
* stop the search. * pass the max_cpos, stop the search.
*/ */
if ((!(rec->e_flags & OCFS2_EXT_REFCOUNTED)) || 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; break;
leaf_clusters = le16_to_cpu(rec->e_leaf_clusters); leaf_clusters = le16_to_cpu(rec->e_leaf_clusters);
rec_end = le32_to_cpu(rec->e_cpos) + 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 * How many clusters do we actually need from
...@@ -3184,12 +3193,13 @@ static int ocfs2_replace_cow(struct inode *inode, ...@@ -3184,12 +3193,13 @@ static int ocfs2_replace_cow(struct inode *inode,
} }
/* /*
* Starting at cpos, try to CoW write_len clusters. * Starting at cpos, try to CoW write_len clusters. Don't CoW
* This will stop when it runs into a hole or an unrefcounted extent. * 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, static int ocfs2_refcount_cow_hunk(struct inode *inode,
struct buffer_head *di_bh, struct buffer_head *di_bh,
u32 cpos, u32 write_len) u32 cpos, u32 write_len, u32 max_cpos)
{ {
int ret; int ret;
u32 cow_start = 0, cow_len = 0; u32 cow_start = 0, cow_len = 0;
...@@ -3201,12 +3211,14 @@ static int ocfs2_refcount_cow_hunk(struct inode *inode, ...@@ -3201,12 +3211,14 @@ static int ocfs2_refcount_cow_hunk(struct inode *inode,
BUG_ON(!(oi->ip_dyn_features & OCFS2_HAS_REFCOUNT_FL)); 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); &cow_start, &cow_len);
if (ret) { if (ret) {
mlog_errno(ret); mlog_errno(ret);
goto out; goto out;
} }
mlog(0, "CoW inode %lu, cpos %u, write_len %u, cow_start %u, " mlog(0, "CoW inode %lu, cpos %u, write_len %u, cow_start %u, "
"cow_len %u\n", inode->i_ino, "cow_len %u\n", inode->i_ino,
cpos, write_len, cow_start, cow_len); cpos, write_len, cow_start, cow_len);
...@@ -3233,12 +3245,12 @@ out: ...@@ -3233,12 +3245,12 @@ out:
/* /*
* CoW any and all clusters between cpos and cpos+write_len. * CoW any and all clusters between cpos and cpos+write_len.
* If this returns successfully, all clusters between cpos and * Don't CoW past max_cpos. If this returns successfully, all
* cpos+write_len are safe to modify. * clusters between cpos and cpos+write_len are safe to modify.
*/ */
int ocfs2_refcount_cow(struct inode *inode, int ocfs2_refcount_cow(struct inode *inode,
struct buffer_head *di_bh, struct buffer_head *di_bh,
u32 cpos, u32 write_len) u32 cpos, u32 write_len, u32 max_cpos)
{ {
int ret = 0; int ret = 0;
u32 p_cluster, num_clusters; u32 p_cluster, num_clusters;
...@@ -3257,7 +3269,7 @@ int ocfs2_refcount_cow(struct inode *inode, ...@@ -3257,7 +3269,7 @@ int ocfs2_refcount_cow(struct inode *inode,
if (ext_flags & OCFS2_EXT_REFCOUNTED) { if (ext_flags & OCFS2_EXT_REFCOUNTED) {
ret = ocfs2_refcount_cow_hunk(inode, di_bh, cpos, ret = ocfs2_refcount_cow_hunk(inode, di_bh, cpos,
num_clusters); num_clusters, max_cpos);
if (ret) { if (ret) {
mlog_errno(ret); mlog_errno(ret);
break; break;
......
...@@ -53,5 +53,5 @@ int ocfs2_prepare_refcount_change_for_del(struct inode *inode, ...@@ -53,5 +53,5 @@ int ocfs2_prepare_refcount_change_for_del(struct inode *inode,
int *credits, int *credits,
struct ocfs2_alloc_context **meta_ac); struct ocfs2_alloc_context **meta_ac);
int ocfs2_refcount_cow(struct inode *inode, struct buffer_head *di_bh, 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 */ #endif /* OCFS2_REFCOUNTTREE_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment