aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ocfs2
diff options
context:
space:
mode:
authorJunxiao Bi <junxiao.bi@oracle.com>2014-12-18 19:17:32 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2014-12-18 22:08:11 -0500
commitf62f12b3a426c8f65b10011b1ec40ba4277cbf5f (patch)
tree08e41966685e3bd305089e9a5aed257a2272461c /fs/ocfs2
parentd82fa87d2b60e8affea3b244ad23c5d9a59c584a (diff)
ocfs2: reflink: fix slow unlink for refcounted file
When running ocfs2 test suite multiple nodes reflink stress test, for a 4 nodes cluster, every unlink() for refcounted file needs about 700s. The slow unlink is caused by the contention of refcount tree lock since all nodes are unlink files using the same refcount tree. When the unlinking file have many extents(over 1600 in our test), most of the extents has refcounted flag set. In ocfs2_commit_truncate(), it will execute the following call trace for every extents. This means it needs get and released refcount tree lock about 1600 times. And when several nodes are do this at the same time, the performance will be very low. ocfs2_remove_btree_range() -- ocfs2_lock_refcount_tree() ---- ocfs2_refcount_lock() ------ __ocfs2_cluster_lock() ocfs2_refcount_lock() is costly, move it to ocfs2_commit_truncate() to do lock/unlock once can improve a lot performance. Signed-off-by: Junxiao Bi <junxiao.bi@oracle.com> Cc: Wengang <wen.gang.wang@oracle.com> Reviewed-by: Mark Fasheh <mfasheh@suse.de> Cc: Joel Becker <jlbec@evilplan.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/ocfs2')
-rw-r--r--fs/ocfs2/alloc.c28
-rw-r--r--fs/ocfs2/alloc.h2
-rw-r--r--fs/ocfs2/dir.c2
-rw-r--r--fs/ocfs2/file.c2
4 files changed, 24 insertions, 10 deletions
diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c
index a93bf9892256..fcae9ef1a328 100644
--- a/fs/ocfs2/alloc.c
+++ b/fs/ocfs2/alloc.c
@@ -5662,7 +5662,7 @@ int ocfs2_remove_btree_range(struct inode *inode,
5662 struct ocfs2_extent_tree *et, 5662 struct ocfs2_extent_tree *et,
5663 u32 cpos, u32 phys_cpos, u32 len, int flags, 5663 u32 cpos, u32 phys_cpos, u32 len, int flags,
5664 struct ocfs2_cached_dealloc_ctxt *dealloc, 5664 struct ocfs2_cached_dealloc_ctxt *dealloc,
5665 u64 refcount_loc) 5665 u64 refcount_loc, bool refcount_tree_locked)
5666{ 5666{
5667 int ret, credits = 0, extra_blocks = 0; 5667 int ret, credits = 0, extra_blocks = 0;
5668 u64 phys_blkno = ocfs2_clusters_to_blocks(inode->i_sb, phys_cpos); 5668 u64 phys_blkno = ocfs2_clusters_to_blocks(inode->i_sb, phys_cpos);
@@ -5676,11 +5676,13 @@ int ocfs2_remove_btree_range(struct inode *inode,
5676 BUG_ON(!(OCFS2_I(inode)->ip_dyn_features & 5676 BUG_ON(!(OCFS2_I(inode)->ip_dyn_features &
5677 OCFS2_HAS_REFCOUNT_FL)); 5677 OCFS2_HAS_REFCOUNT_FL));
5678 5678
5679 ret = ocfs2_lock_refcount_tree(osb, refcount_loc, 1, 5679 if (!refcount_tree_locked) {
5680 &ref_tree, NULL); 5680 ret = ocfs2_lock_refcount_tree(osb, refcount_loc, 1,
5681 if (ret) { 5681 &ref_tree, NULL);
5682 mlog_errno(ret); 5682 if (ret) {
5683 goto bail; 5683 mlog_errno(ret);
5684 goto bail;
5685 }
5684 } 5686 }
5685 5687
5686 ret = ocfs2_prepare_refcount_change_for_del(inode, 5688 ret = ocfs2_prepare_refcount_change_for_del(inode,
@@ -7021,6 +7023,7 @@ int ocfs2_commit_truncate(struct ocfs2_super *osb,
7021 u64 refcount_loc = le64_to_cpu(di->i_refcount_loc); 7023 u64 refcount_loc = le64_to_cpu(di->i_refcount_loc);
7022 struct ocfs2_extent_tree et; 7024 struct ocfs2_extent_tree et;
7023 struct ocfs2_cached_dealloc_ctxt dealloc; 7025 struct ocfs2_cached_dealloc_ctxt dealloc;
7026 struct ocfs2_refcount_tree *ref_tree = NULL;
7024 7027
7025 ocfs2_init_dinode_extent_tree(&et, INODE_CACHE(inode), di_bh); 7028 ocfs2_init_dinode_extent_tree(&et, INODE_CACHE(inode), di_bh);
7026 ocfs2_init_dealloc_ctxt(&dealloc); 7029 ocfs2_init_dealloc_ctxt(&dealloc);
@@ -7130,9 +7133,18 @@ start:
7130 7133
7131 phys_cpos = ocfs2_blocks_to_clusters(inode->i_sb, blkno); 7134 phys_cpos = ocfs2_blocks_to_clusters(inode->i_sb, blkno);
7132 7135
7136 if ((flags & OCFS2_EXT_REFCOUNTED) && trunc_len && !ref_tree) {
7137 status = ocfs2_lock_refcount_tree(osb, refcount_loc, 1,
7138 &ref_tree, NULL);
7139 if (status) {
7140 mlog_errno(status);
7141 goto bail;
7142 }
7143 }
7144
7133 status = ocfs2_remove_btree_range(inode, &et, trunc_cpos, 7145 status = ocfs2_remove_btree_range(inode, &et, trunc_cpos,
7134 phys_cpos, trunc_len, flags, &dealloc, 7146 phys_cpos, trunc_len, flags, &dealloc,
7135 refcount_loc); 7147 refcount_loc, true);
7136 if (status < 0) { 7148 if (status < 0) {
7137 mlog_errno(status); 7149 mlog_errno(status);
7138 goto bail; 7150 goto bail;
@@ -7147,6 +7159,8 @@ start:
7147 goto start; 7159 goto start;
7148 7160
7149bail: 7161bail:
7162 if (ref_tree)
7163 ocfs2_unlock_refcount_tree(osb, ref_tree, 1);
7150 7164
7151 ocfs2_schedule_truncate_log_flush(osb, 1); 7165 ocfs2_schedule_truncate_log_flush(osb, 1);
7152 7166
diff --git a/fs/ocfs2/alloc.h b/fs/ocfs2/alloc.h
index ca381c584127..fb09b97db162 100644
--- a/fs/ocfs2/alloc.h
+++ b/fs/ocfs2/alloc.h
@@ -142,7 +142,7 @@ int ocfs2_remove_btree_range(struct inode *inode,
142 struct ocfs2_extent_tree *et, 142 struct ocfs2_extent_tree *et,
143 u32 cpos, u32 phys_cpos, u32 len, int flags, 143 u32 cpos, u32 phys_cpos, u32 len, int flags,
144 struct ocfs2_cached_dealloc_ctxt *dealloc, 144 struct ocfs2_cached_dealloc_ctxt *dealloc,
145 u64 refcount_loc); 145 u64 refcount_loc, bool refcount_tree_locked);
146 146
147int ocfs2_num_free_extents(struct ocfs2_super *osb, 147int ocfs2_num_free_extents(struct ocfs2_super *osb,
148 struct ocfs2_extent_tree *et); 148 struct ocfs2_extent_tree *et);
diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c
index 79d56dc981bc..319e786175af 100644
--- a/fs/ocfs2/dir.c
+++ b/fs/ocfs2/dir.c
@@ -4479,7 +4479,7 @@ int ocfs2_dx_dir_truncate(struct inode *dir, struct buffer_head *di_bh)
4479 p_cpos = ocfs2_blocks_to_clusters(dir->i_sb, blkno); 4479 p_cpos = ocfs2_blocks_to_clusters(dir->i_sb, blkno);
4480 4480
4481 ret = ocfs2_remove_btree_range(dir, &et, cpos, p_cpos, clen, 0, 4481 ret = ocfs2_remove_btree_range(dir, &et, cpos, p_cpos, clen, 0,
4482 &dealloc, 0); 4482 &dealloc, 0, false);
4483 if (ret) { 4483 if (ret) {
4484 mlog_errno(ret); 4484 mlog_errno(ret);
4485 goto out; 4485 goto out;
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index 69fb9f75b082..3950693dd0f6 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -1803,7 +1803,7 @@ static int ocfs2_remove_inode_range(struct inode *inode,
1803 1803
1804 ret = ocfs2_remove_btree_range(inode, &et, trunc_cpos, 1804 ret = ocfs2_remove_btree_range(inode, &et, trunc_cpos,
1805 phys_cpos, trunc_len, flags, 1805 phys_cpos, trunc_len, flags,
1806 &dealloc, refcount_loc); 1806 &dealloc, refcount_loc, false);
1807 if (ret < 0) { 1807 if (ret < 0) {
1808 mlog_errno(ret); 1808 mlog_errno(ret);
1809 goto out; 1809 goto out;