aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorTao Ma <tao.ma@oracle.com>2009-08-25 21:47:28 -0400
committerJoel Becker <joel.becker@oracle.com>2009-09-22 23:09:38 -0400
commit37f8a2bfaa8364dd3644cccee8824bb8f5e409a5 (patch)
treeab3083179c621c3e8d0be3980aaed96528f90599 /fs
parent293b2f70b4a16a1ca91efd28ef3d6634262c6887 (diff)
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 <tao.ma@oracle.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/ocfs2/aops.c2
-rw-r--r--fs/ocfs2/file.c46
-rw-r--r--fs/ocfs2/refcounttree.c34
-rw-r--r--fs/ocfs2/refcounttree.h2
4 files changed, 70 insertions, 14 deletions
diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c
index 9db9d64ca475..33e03c551127 100644
--- a/fs/ocfs2/aops.c
+++ b/fs/ocfs2/aops.c
@@ -1712,7 +1712,7 @@ int ocfs2_write_begin_nolock(struct address_space *mapping,
1712 goto out; 1712 goto out;
1713 } else if (ret == 1) { 1713 } else if (ret == 1) {
1714 ret = ocfs2_refcount_cow(inode, di_bh, 1714 ret = ocfs2_refcount_cow(inode, di_bh,
1715 wc->w_cpos, wc->w_clen); 1715 wc->w_cpos, wc->w_clen, UINT_MAX);
1716 if (ret) { 1716 if (ret) {
1717 mlog_errno(ret); 1717 mlog_errno(ret);
1718 goto out; 1718 goto out;
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index 6ee20e82bcc5..75f5b81805b5 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -335,6 +335,39 @@ out:
335 return ret; 335 return ret;
336} 336}
337 337
338static int ocfs2_cow_file_pos(struct inode *inode,
339 struct buffer_head *fe_bh,
340 u64 offset)
341{
342 int status;
343 u32 phys, cpos = offset >> OCFS2_SB(inode->i_sb)->s_clustersize_bits;
344 unsigned int num_clusters = 0;
345 unsigned int ext_flags = 0;
346
347 /*
348 * If the new offset is aligned to the range of the cluster, there is
349 * no space for ocfs2_zero_range_for_truncate to fill, so no need to
350 * CoW either.
351 */
352 if ((offset & (OCFS2_SB(inode->i_sb)->s_clustersize - 1)) == 0)
353 return 0;
354
355 status = ocfs2_get_clusters(inode, cpos, &phys,
356 &num_clusters, &ext_flags);
357 if (status) {
358 mlog_errno(status);
359 goto out;
360 }
361
362 if (!(ext_flags & OCFS2_EXT_REFCOUNTED))
363 goto out;
364
365 return ocfs2_refcount_cow(inode, fe_bh, cpos, 1, cpos+1);
366
367out:
368 return status;
369}
370
338static int ocfs2_orphan_for_truncate(struct ocfs2_super *osb, 371static int ocfs2_orphan_for_truncate(struct ocfs2_super *osb,
339 struct inode *inode, 372 struct inode *inode,
340 struct buffer_head *fe_bh, 373 struct buffer_head *fe_bh,
@@ -347,6 +380,17 @@ static int ocfs2_orphan_for_truncate(struct ocfs2_super *osb,
347 380
348 mlog_entry_void(); 381 mlog_entry_void();
349 382
383 /*
384 * We need to CoW the cluster contains the offset if it is reflinked
385 * since we will call ocfs2_zero_range_for_truncate later which will
386 * write "0" from offset to the end of the cluster.
387 */
388 status = ocfs2_cow_file_pos(inode, fe_bh, new_i_size);
389 if (status) {
390 mlog_errno(status);
391 return status;
392 }
393
350 /* TODO: This needs to actually orphan the inode in this 394 /* TODO: This needs to actually orphan the inode in this
351 * transaction. */ 395 * transaction. */
352 396
@@ -1713,7 +1757,7 @@ static int ocfs2_prepare_inode_for_refcount(struct inode *inode,
1713 1757
1714 *meta_level = 1; 1758 *meta_level = 1;
1715 1759
1716 ret = ocfs2_refcount_cow(inode, di_bh, cpos, clusters); 1760 ret = ocfs2_refcount_cow(inode, di_bh, cpos, clusters, UINT_MAX);
1717 if (ret) 1761 if (ret)
1718 mlog_errno(ret); 1762 mlog_errno(ret);
1719out: 1763out:
diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c
index 0a92436557e3..37aa0c8696d6 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,
2482 * 2482 *
2483 * cpos is vitual start cluster position we want to do CoW in a 2483 * cpos is vitual start cluster position we want to do CoW in a
2484 * file and write_len is the cluster length. 2484 * file and write_len is the cluster length.
2485 * max_cpos is the place where we want to stop CoW intentionally.
2485 * 2486 *
2486 * Normal we will start CoW from the beginning of extent record cotaining cpos. 2487 * Normal we will start CoW from the beginning of extent record cotaining cpos.
2487 * We try to break up extents on boundaries of MAX_CONTIG_BYTES so that we 2488 * 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 struct buffer_head *di_bh, 2492 struct buffer_head *di_bh,
2492 u32 cpos, 2493 u32 cpos,
2493 u32 write_len, 2494 u32 write_len,
2495 u32 max_cpos,
2494 u32 *cow_start, 2496 u32 *cow_start,
2495 u32 *cow_len) 2497 u32 *cow_len)
2496{ 2498{
@@ -2505,6 +2507,8 @@ static int ocfs2_refcount_cal_cow_clusters(struct inode *inode,
2505 int contig_clusters = ocfs2_cow_contig_clusters(inode->i_sb); 2507 int contig_clusters = ocfs2_cow_contig_clusters(inode->i_sb);
2506 int leaf_clusters; 2508 int leaf_clusters;
2507 2509
2510 BUG_ON(cpos + write_len > max_cpos);
2511
2508 if (tree_height > 0) { 2512 if (tree_height > 0) {
2509 ret = ocfs2_find_leaf(INODE_CACHE(inode), el, cpos, &eb_bh); 2513 ret = ocfs2_find_leaf(INODE_CACHE(inode), el, cpos, &eb_bh);
2510 if (ret) { 2514 if (ret) {
@@ -2549,15 +2553,20 @@ static int ocfs2_refcount_cal_cow_clusters(struct inode *inode,
2549 } 2553 }
2550 2554
2551 /* 2555 /*
2552 * If we encounter a hole or a non-refcounted record, 2556 * If we encounter a hole, a non-refcounted record or
2553 * stop the search. 2557 * pass the max_cpos, stop the search.
2554 */ 2558 */
2555 if ((!(rec->e_flags & OCFS2_EXT_REFCOUNTED)) || 2559 if ((!(rec->e_flags & OCFS2_EXT_REFCOUNTED)) ||
2556 (*cow_len && rec_end != le32_to_cpu(rec->e_cpos))) 2560 (*cow_len && rec_end != le32_to_cpu(rec->e_cpos)) ||
2561 (max_cpos <= le32_to_cpu(rec->e_cpos)))
2557 break; 2562 break;
2558 2563
2559 leaf_clusters = le16_to_cpu(rec->e_leaf_clusters); 2564 leaf_clusters = le16_to_cpu(rec->e_leaf_clusters);
2560 rec_end = le32_to_cpu(rec->e_cpos) + leaf_clusters; 2565 rec_end = le32_to_cpu(rec->e_cpos) + leaf_clusters;
2566 if (rec_end > max_cpos) {
2567 rec_end = max_cpos;
2568 leaf_clusters = rec_end - le32_to_cpu(rec->e_cpos);
2569 }
2561 2570
2562 /* 2571 /*
2563 * How many clusters do we actually need from 2572 * How many clusters do we actually need from
@@ -3184,12 +3193,13 @@ static int ocfs2_replace_cow(struct inode *inode,
3184} 3193}
3185 3194
3186/* 3195/*
3187 * Starting at cpos, try to CoW write_len clusters. 3196 * Starting at cpos, try to CoW write_len clusters. Don't CoW
3188 * This will stop when it runs into a hole or an unrefcounted extent. 3197 * past max_cpos. This will stop when it runs into a hole or an
3198 * unrefcounted extent.
3189 */ 3199 */
3190static int ocfs2_refcount_cow_hunk(struct inode *inode, 3200static int ocfs2_refcount_cow_hunk(struct inode *inode,
3191 struct buffer_head *di_bh, 3201 struct buffer_head *di_bh,
3192 u32 cpos, u32 write_len) 3202 u32 cpos, u32 write_len, u32 max_cpos)
3193{ 3203{
3194 int ret; 3204 int ret;
3195 u32 cow_start = 0, cow_len = 0; 3205 u32 cow_start = 0, cow_len = 0;
@@ -3201,12 +3211,14 @@ static int ocfs2_refcount_cow_hunk(struct inode *inode,
3201 3211
3202 BUG_ON(!(oi->ip_dyn_features & OCFS2_HAS_REFCOUNT_FL)); 3212 BUG_ON(!(oi->ip_dyn_features & OCFS2_HAS_REFCOUNT_FL));
3203 3213
3204 ret = ocfs2_refcount_cal_cow_clusters(inode, di_bh, cpos, write_len, 3214 ret = ocfs2_refcount_cal_cow_clusters(inode, di_bh,
3215 cpos, write_len, max_cpos,
3205 &cow_start, &cow_len); 3216 &cow_start, &cow_len);
3206 if (ret) { 3217 if (ret) {
3207 mlog_errno(ret); 3218 mlog_errno(ret);
3208 goto out; 3219 goto out;
3209 } 3220 }
3221
3210 mlog(0, "CoW inode %lu, cpos %u, write_len %u, cow_start %u, " 3222 mlog(0, "CoW inode %lu, cpos %u, write_len %u, cow_start %u, "
3211 "cow_len %u\n", inode->i_ino, 3223 "cow_len %u\n", inode->i_ino,
3212 cpos, write_len, cow_start, cow_len); 3224 cpos, write_len, cow_start, cow_len);
@@ -3233,12 +3245,12 @@ out:
3233 3245
3234/* 3246/*
3235 * CoW any and all clusters between cpos and cpos+write_len. 3247 * CoW any and all clusters between cpos and cpos+write_len.
3236 * If this returns successfully, all clusters between cpos and 3248 * Don't CoW past max_cpos. If this returns successfully, all
3237 * cpos+write_len are safe to modify. 3249 * clusters between cpos and cpos+write_len are safe to modify.
3238 */ 3250 */
3239int ocfs2_refcount_cow(struct inode *inode, 3251int ocfs2_refcount_cow(struct inode *inode,
3240 struct buffer_head *di_bh, 3252 struct buffer_head *di_bh,
3241 u32 cpos, u32 write_len) 3253 u32 cpos, u32 write_len, u32 max_cpos)
3242{ 3254{
3243 int ret = 0; 3255 int ret = 0;
3244 u32 p_cluster, num_clusters; 3256 u32 p_cluster, num_clusters;
@@ -3257,7 +3269,7 @@ int ocfs2_refcount_cow(struct inode *inode,
3257 3269
3258 if (ext_flags & OCFS2_EXT_REFCOUNTED) { 3270 if (ext_flags & OCFS2_EXT_REFCOUNTED) {
3259 ret = ocfs2_refcount_cow_hunk(inode, di_bh, cpos, 3271 ret = ocfs2_refcount_cow_hunk(inode, di_bh, cpos,
3260 num_clusters); 3272 num_clusters, max_cpos);
3261 if (ret) { 3273 if (ret) {
3262 mlog_errno(ret); 3274 mlog_errno(ret);
3263 break; 3275 break;
diff --git a/fs/ocfs2/refcounttree.h b/fs/ocfs2/refcounttree.h
index a8c15b0b2307..356f99c85635 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,
53 int *credits, 53 int *credits,
54 struct ocfs2_alloc_context **meta_ac); 54 struct ocfs2_alloc_context **meta_ac);
55int ocfs2_refcount_cow(struct inode *inode, struct buffer_head *di_bh, 55int ocfs2_refcount_cow(struct inode *inode, struct buffer_head *di_bh,
56 u32 cpos, u32 write_len); 56 u32 cpos, u32 write_len, u32 max_cpos);
57#endif /* OCFS2_REFCOUNTTREE_H */ 57#endif /* OCFS2_REFCOUNTTREE_H */