Skip to contentSkip to conversation

fix dead lock caused by ocfs2_defrag_extent

PATCH In review Larry Chen 3 participants 9 messages Aug 27, 2018 – Sep 2, 2018 2 messages not in the archive OCFS2

lore
Share & tools
lore

Larry Chen

author
#0 lore raw

PATCH 1/1 fix dead lock caused by ocfs2_defrag_extent

ocfs2_defrag_extent may fall into dead lock.

ocfs2_ioctl_move_extents
ocfs2_ioctl_move_extents
ocfs2_move_extents
ocfs2_defrag_extent
ocfs2_lock_allocators_move_extents

ocfs2_reserve_clusters
inode_lock GLOBAL_BITMAP_SYSTEM_INODE

__ocfs2_flush_truncate_log
inode_lock GLOBAL_BITMAP_SYSTEM_INODE

As back trace shows above, ocfs2_reserve_clusters will call inode_lock against the global bitmap if local allocator has not sufficient cluters. Once global bitmap could meet the demand, ocfs2_reserve_cluster will return success with global bitmap locked.

After ocfs2_reserve_cluster, if truncate log is full, __ocfs2_flush_truncate_log will definitely fall into dead lock because it needs to inode_lock global bitmap, which has already been locked.

To fix this bug, we could remove from ocfs2_lock_allocators_move_extents the code which intends to lock global allocator, and put the removed code after __ocfs2_flush_truncate_log

The ocfs2_lock_allocators_move_extents has been refered by 2 places, one is here, the other does not need the data allocator context, which means this patch does not affect the caller so far.

---
 fs/ocfs2/move_extents.c | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)
fs/ocfs2/move_extents.c +6 -7
index 7eb3b0a6347e..064dedf40d74 100644
@@ -192,13 +192,6 @@ static int ocfs2_lock_allocators_move_extents(struct inode *inode,
 		goto out;
 	}
 
-	if (data_ac) {
-		ret = ocfs2_reserve_clusters(osb, clusters_to_move, data_ac);
-		if (ret) {
-			mlog_errno(ret);
-			goto out;
-		}
-	}
 
 	*credits += ocfs2_calc_extend_credits(osb->sb, et->et_root_el);
 
@@ -283,6 +276,12 @@ static int ocfs2_defrag_extent(struct ocfs2_move_extents_context *context,
 		}
 	}
 
+	ret = ocfs2_reserve_clusters(osb, *len, &context->data_ac);
+	if (ret) {
+		mlog_errno(ret);
+		goto out_unlock_mutex;
+	}
+
 	handle = ocfs2_start_trans(osb, credits);
 	if (IS_ERR(handle)) {
 		ret = PTR_ERR(handle);
Signature · 1 line
2.13.7
#1 lore raw

Hi Larry,

Quoted 24 lines · Larry Chen

ocfs2_defrag_extent may fall into dead lock.

ocfs2_ioctl_move_extents
ocfs2_ioctl_move_extents
ocfs2_move_extents
ocfs2_defrag_extent
ocfs2_lock_allocators_move_extents

ocfs2_reserve_clusters
inode_lock GLOBAL_BITMAP_SYSTEM_INODE

__ocfs2_flush_truncate_log
inode_lock GLOBAL_BITMAP_SYSTEM_INODE

As back trace shows above, ocfs2_reserve_clusters will call inode_lock against the global bitmap if local allocator has not sufficient cluters. Once global bitmap could meet the demand, ocfs2_reserve_cluster will return success with global bitmap locked.

After ocfs2_reserve_cluster, if truncate log is full, __ocfs2_flush_truncate_log will definitely fall into dead lock because it needs to inode_lock global bitmap, which has already been locked.

Quoted 8 lines

To fix this bug, we could remove from ocfs2_lock_allocators_move_extents the code which intends to lock global allocator, and put the removed code after __ocfs2_flush_truncate_log

The ocfs2_lock_allocators_move_extents has been refered by 2 places, one is here, the other does not need the data allocator context, which means this patch does not affect the caller so far.

After this change, data_ac in ocfs2_lock_allocators_move_extents() is no longer used. So I'd prefer we also clean it up.

Thanks,
Joseph

Quoted 39 lines
---
 fs/ocfs2/move_extents.c | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)
fs/ocfs2/move_extents.c +6 -7
index 7eb3b0a6347e..064dedf40d74 100644
@@ -192,13 +192,6 @@ static int ocfs2_lock_allocators_move_extents(struct inode *inode,
 		goto out;
 	}
 
-	if (data_ac) {
-		ret = ocfs2_reserve_clusters(osb, clusters_to_move, data_ac);
-		if (ret) {
-			mlog_errno(ret);
-			goto out;
-		}
-	}
 
 	*credits += ocfs2_calc_extend_credits(osb->sb, et->et_root_el);
 
@@ -283,6 +276,12 @@ static int ocfs2_defrag_extent(struct ocfs2_move_extents_context *context,
 		}
 	}
 
+	ret = ocfs2_reserve_clusters(osb, *len, &context->data_ac);
+	if (ret) {
+		mlog_errno(ret);
+		goto out_unlock_mutex;
+	}
+
 	handle = ocfs2_start_trans(osb, credits);
 	if (IS_ERR(handle)) {
 		ret = PTR_ERR(handle);
author
#2 lore raw

PATCH 1/1 fix dead lock caused by ocfs2_defrag_extent

Hi Joseph,

Thanks for your review, I'll remove it later.

Thanks,
Larry

Quoted 81 lines · Joseph Qi

Hi Larry,

Quoted 24 lines · Larry Chen

ocfs2_defrag_extent may fall into dead lock.

ocfs2_ioctl_move_extents
ocfs2_ioctl_move_extents
ocfs2_move_extents
ocfs2_defrag_extent
ocfs2_lock_allocators_move_extents

ocfs2_reserve_clusters
inode_lock GLOBAL_BITMAP_SYSTEM_INODE

__ocfs2_flush_truncate_log
inode_lock GLOBAL_BITMAP_SYSTEM_INODE

As back trace shows above, ocfs2_reserve_clusters will call inode_lock against the global bitmap if local allocator has not sufficient cluters. Once global bitmap could meet the demand, ocfs2_reserve_cluster will return success with global bitmap locked.

After ocfs2_reserve_cluster, if truncate log is full, __ocfs2_flush_truncate_log will definitely fall into dead lock because it needs to inode_lock global bitmap, which has already been locked.

Quoted 8 lines

To fix this bug, we could remove from ocfs2_lock_allocators_move_extents the code which intends to lock global allocator, and put the removed code after __ocfs2_flush_truncate_log

The ocfs2_lock_allocators_move_extents has been refered by 2 places, one is here, the other does not need the data allocator context, which means this patch does not affect the caller so far.

After this change, data_ac in ocfs2_lock_allocators_move_extents() is no longer used. So I'd prefer we also clean it up.

Thanks,
Joseph

Quoted 39 lines
---
  fs/ocfs2/move_extents.c | 13 ++++++-------
  1 file changed, 6 insertions(+), 7 deletions(-)
fs/ocfs2/move_extents.c +6 -7
index 7eb3b0a6347e..064dedf40d74 100644
@@ -192,13 +192,6 @@ static int ocfs2_lock_allocators_move_extents(struct inode *inode,
  		goto out;
  	}
 
-	if (data_ac) {
-		ret = ocfs2_reserve_clusters(osb, clusters_to_move, data_ac);
-		if (ret) {
-			mlog_errno(ret);
-			goto out;
-		}
-	}
 
  	*credits += ocfs2_calc_extend_credits(osb->sb, et->et_root_el);
 
@@ -283,6 +276,12 @@ static int ocfs2_defrag_extent(struct ocfs2_move_extents_context *context,
  		}
  	}
 
+	ret = ocfs2_reserve_clusters(osb, *len, &context->data_ac);
+	if (ret) {
+		mlog_errno(ret);
+		goto out_unlock_mutex;
+	}
+
  	handle = ocfs2_start_trans(osb, credits);
  	if (IS_ERR(handle)) {
  		ret = PTR_ERR(handle);
#3 lore raw
In reply to Larry Chen

Hi Larry,

I'd like to propose another way to fix this issue, which might be easier. Actually, ocfs2_reserve_clusters() inside will flush truncate log if there is no enough clusters left.
So how about just remove __ocfs2_flush_truncate_log() in ocfs2_defrag_extent()?

Thanks,
Changwei

Quoted 70 lines · Larry Chen

ocfs2_defrag_extent may fall into dead lock.

ocfs2_ioctl_move_extents
ocfs2_ioctl_move_extents
ocfs2_move_extents
ocfs2_defrag_extent
ocfs2_lock_allocators_move_extents

ocfs2_reserve_clusters
inode_lock GLOBAL_BITMAP_SYSTEM_INODE

__ocfs2_flush_truncate_log
inode_lock GLOBAL_BITMAP_SYSTEM_INODE

As back trace shows above, ocfs2_reserve_clusters will call inode_lock against the global bitmap if local allocator has not sufficient cluters. Once global bitmap could meet the demand, ocfs2_reserve_cluster will return success with global bitmap locked.

After ocfs2_reserve_cluster, if truncate log is full, __ocfs2_flush_truncate_log will definitely fall into dead lock because it needs to inode_lock global bitmap, which has already been locked.

To fix this bug, we could remove from ocfs2_lock_allocators_move_extents the code which intends to lock global allocator, and put the removed code after __ocfs2_flush_truncate_log

The ocfs2_lock_allocators_move_extents has been refered by 2 places, one is here, the other does not need the data allocator context, which means this patch does not affect the caller so far.

---
  fs/ocfs2/move_extents.c | 13 ++++++-------
  1 file changed, 6 insertions(+), 7 deletions(-)
fs/ocfs2/move_extents.c +6 -7
index 7eb3b0a6347e..064dedf40d74 100644
@@ -192,13 +192,6 @@ static int ocfs2_lock_allocators_move_extents(struct inode *inode,
  		goto out;
  	}
 
-	if (data_ac) {
-		ret = ocfs2_reserve_clusters(osb, clusters_to_move, data_ac);
-		if (ret) {
-			mlog_errno(ret);
-			goto out;
-		}
-	}
 
  	*credits += ocfs2_calc_extend_credits(osb->sb, et->et_root_el);
 
@@ -283,6 +276,12 @@ static int ocfs2_defrag_extent(struct ocfs2_move_extents_context *context,
  		}
  	}
 
+	ret = ocfs2_reserve_clusters(osb, *len, &context->data_ac);
+	if (ret) {
+		mlog_errno(ret);
+		goto out_unlock_mutex;
+	}
+
  	handle = ocfs2_start_trans(osb, credits);
  	if (IS_ERR(handle)) {
  		ret = PTR_ERR(handle);

A message that is not in the archive (sent off-list or not yet received): HK0PR06MB2561A612E5A95B60E7F8E22FD50A0@HK0PR06MB2561.apcprd06.prod.outlook.com

A message that is not in the archive (sent off-list or not yet received): [email protected]

#6 lore raw

PATCH 1/1 fix dead lock caused by ocfs2_defrag_extent

Hi Larry,

Sorry for this late reply.

Quoted 10 lines · Larry Chen

Hi Changwei,

I think there might be some logic error.

Imagine that we remove __ocfs2_flush_truncate_log() in ocfs2_defrag_extent().

In ocfs2_reserve_clusters might skip freeing truncate log if there is enough clusters in local. Then __ocfs2_move_extent might return error because ocfs2_truncate_log_append might fail.

I think changing ocfs2 infrastructure methods like your way is not a good idea.
So how about this way?
->lock truncate log inode
->__ocfs2_flush_truncate_log() ->ocfs2_lock_allocators_move_extents()
->unlock truncate log inode

Thanks
Changwei

Quoted 94 lines

Thanks,
Larry

Quoted 87 lines · Changwei Ge

Hi Larry,

I'd like to propose another way to fix this issue, which might be easier. :-)
Actually, ocfs2_reserve_clusters() inside will flush truncate log if there is no enough clusters left.
So how about just remove __ocfs2_flush_truncate_log() in ocfs2_defrag_extent()?

Thanks,
Changwei

Quoted 73 lines · Larry Chen

ocfs2_defrag_extent may fall into dead lock.

ocfs2_ioctl_move_extents
??? ocfs2_ioctl_move_extents
????? ocfs2_move_extents
??????? ocfs2_defrag_extent
????????? ocfs2_lock_allocators_move_extents

??????????? ocfs2_reserve_clusters ????????????? inode_lock GLOBAL_BITMAP_SYSTEM_INODE

????? __ocfs2_flush_truncate_log ????????????? inode_lock GLOBAL_BITMAP_SYSTEM_INODE

As back trace shows above, ocfs2_reserve_clusters will call inode_lock against the global bitmap if local allocator has not sufficient cluters.
Once global bitmap could meet the demand, ocfs2_reserve_cluster will return success with global bitmap locked.

After ocfs2_reserve_cluster, if truncate log is full, __ocfs2_flush_truncate_log will definitely fall into dead lock because it needs to inode_lock global bitmap, which has already been locked.

To fix this bug, we could remove from ocfs2_lock_allocators_move_extents
the code which intends to lock global allocator, and put the removed code after __ocfs2_flush_truncate_log

The ocfs2_lock_allocators_move_extents has been refered by 2 places, one
is here, the other does not need the data allocator context, which means
this patch does not affect the caller so far.

---
?? fs/ocfs2/move_extents.c | 13 ++++++------- ?? 1 file changed, 6 insertions(+), 7 deletions(-)

fs/ocfs2/move_extents.c +0 -6
index 7eb3b0a6347e..064dedf40d74 100644
@@ -192,13 +192,6 @@ static int 
 ocfs2_lock_allocators_move_extents(struct inode *inode,
 ?????????? goto out;
 ?????? }
 ?? -??? if (data_ac) {
-??????? ret = ocfs2_reserve_clusters(osb, clusters_to_move, data_ac);
-??????? if (ret) {
-??????????? mlog_errno(ret);
-??????????? goto out;
-??????? }
-??? }
 ?? ?????? *credits += ocfs2_calc_extend_credits(osb->sb, 
 et->et_root_el);

?? @@ -283,6 +276,12 @@ static int ocfs2_defrag_extent(struct ocfs2_move_extents_context *context,
?????????? }
?????? }
?? +??? ret = ocfs2_reserve_clusters(osb, *len, &context->data_ac); +??? if (ret) {
+??????? mlog_errno(ret);
+??????? goto out_unlock_mutex;
+??? }
+
?????? handle = ocfs2_start_trans(osb, credits);
?????? if (IS_ERR(handle)) {
?????????? ret = PTR_ERR(handle);

author
#7 lore raw

PATCH 1/1 fix dead lock caused by ocfs2_defrag_extent

Hi Changwei,

Maybe we need more investigation.

The following is your fix:
lock truncate log inode
__ocfs2_flush_truncate_log()
ocfs2_lock_allocators_move_extents()
unlock truncate log inode

The lock action will happen like following:
lock(truncate_inode)
lock(sub allocat)
lock(local_alloc) or lock(global_bitmap)

I'm not sure if there is another code path that tries to get the same locks but in different order, which may causes dead locks.

Indeed ocfs2 involves too many locks, I would like to reduce the deadlock risk at max extent.

Another way is to add an new argument for __ocfs2_flush_truncate which indicates whether global bitmap is needed to be locked.

Thanks,
Larry

Quoted 122 lines · Changwei Ge

Hi Larry,

Sorry for this late reply.

Quoted 10 lines · Larry Chen

Hi Changwei,

I think there might be some logic error.

Imagine that we remove __ocfs2_flush_truncate_log() in ocfs2_defrag_extent().

In ocfs2_reserve_clusters might skip freeing truncate log if there is enough clusters in local. Then __ocfs2_move_extent might return error because ocfs2_truncate_log_append might fail.

I think changing ocfs2 infrastructure methods like your way is not a good idea.
So how about this way?
->lock truncate log inode
->__ocfs2_flush_truncate_log() ->ocfs2_lock_allocators_move_extents()
->unlock truncate log inode

Thanks
Changwei

Quoted 94 lines

Thanks,
Larry

Quoted 87 lines · Changwei Ge

Hi Larry,

I'd like to propose another way to fix this issue, which might be easier. :-)
Actually, ocfs2_reserve_clusters() inside will flush truncate log if there is no enough clusters left.
So how about just remove __ocfs2_flush_truncate_log() in ocfs2_defrag_extent()?

Thanks,
Changwei

Quoted 73 lines · Larry Chen

ocfs2_defrag_extent may fall into dead lock.

ocfs2_ioctl_move_extents
??? ocfs2_ioctl_move_extents
????? ocfs2_move_extents
??????? ocfs2_defrag_extent
????????? ocfs2_lock_allocators_move_extents

??????????? ocfs2_reserve_clusters ????????????? inode_lock GLOBAL_BITMAP_SYSTEM_INODE

????? __ocfs2_flush_truncate_log ????????????? inode_lock GLOBAL_BITMAP_SYSTEM_INODE

As back trace shows above, ocfs2_reserve_clusters will call inode_lock against the global bitmap if local allocator has not sufficient cluters.
Once global bitmap could meet the demand, ocfs2_reserve_cluster will return success with global bitmap locked.

After ocfs2_reserve_cluster, if truncate log is full, __ocfs2_flush_truncate_log will definitely fall into dead lock because it needs to inode_lock global bitmap, which has already been locked.

To fix this bug, we could remove from ocfs2_lock_allocators_move_extents
the code which intends to lock global allocator, and put the removed code after __ocfs2_flush_truncate_log

The ocfs2_lock_allocators_move_extents has been refered by 2 places, one
is here, the other does not need the data allocator context, which means
this patch does not affect the caller so far.

---
 ?? fs/ocfs2/move_extents.c | 13 ++++++-------

?? 1 file changed, 6 insertions(+), 7 deletions(-)

fs/ocfs2/move_extents.c +0 -6
index 7eb3b0a6347e..064dedf40d74 100644
@@ -192,13 +192,6 @@ static int
 ocfs2_lock_allocators_move_extents(struct inode *inode,
 ?????????? goto out;
 ?????? }
 ?? -??? if (data_ac) {
-??????? ret = ocfs2_reserve_clusters(osb, clusters_to_move, data_ac);
-??????? if (ret) {
-??????????? mlog_errno(ret);
-??????????? goto out;
-??????? }
-??? }
 ?? ?????? *credits += ocfs2_calc_extend_credits(osb->sb,
 et->et_root_el);
 ?? @@ -283,6 +276,12 @@ static int ocfs2_defrag_extent(struct

ocfs2_move_extents_context *context,
?????????? }
?????? }
?? +??? ret = ocfs2_reserve_clusters(osb, *len, &context->data_ac);
+??? if (ret) {
+??????? mlog_errno(ret);
+??????? goto out_unlock_mutex;
+??? }
+
?????? handle = ocfs2_start_trans(osb, credits);
?????? if (IS_ERR(handle)) {
?????????? ret = PTR_ERR(handle);

#8 lore raw

PATCH 1/1 fix dead lock caused by ocfs2_defrag_extent

Hi Larry,

Quoted 10 lines · Larry Chen

Hi Changwei,

Maybe we need more investigation.

The following is your fix:
lock truncate log inode
??? __ocfs2_flush_truncate_log()
????ocfs2_lock_allocators_move_extents()
??????? unlock truncate log inode

Quoted 4 lines

The lock action will happen like following:
lock(truncate_inode)
????lock(sub allocat)
????lock(local_alloc) or lock(global_bitmap)

I don't think we have to worry much about mixed order? of cluster lock and inode mutex since cluster lock granted node will directly succeed instead of waiting for itself.

Quoted 9 lines

I'm not sure if there is another code path that tries to get the same locks but in different order, which may causes dead locks.

Indeed ocfs2 involves too many locks, I would like to reduce the deadlock risk at max extent.

Another way is to add an new argument for __ocfs2_flush_truncate which indicates whether global bitmap is needed to be locked.

Sounds a feasible way :)

Thanks,
Changwei

Quoted 135 lines

Thanks,
Larry

Quoted 124 lines · Changwei Ge

Hi Larry,

Sorry for this late reply.

Quoted 10 lines · Larry Chen

Hi Changwei,

I think there might be some logic error.

Imagine that we remove __ocfs2_flush_truncate_log() in ocfs2_defrag_extent().

In ocfs2_reserve_clusters might skip freeing truncate log if there is enough clusters in local. Then __ocfs2_move_extent might return error because ocfs2_truncate_log_append might fail.

I think changing ocfs2 infrastructure methods like your way is not a good idea.
So how about this way?
->lock truncate log inode
->__ocfs2_flush_truncate_log() ->ocfs2_lock_allocators_move_extents()
->unlock truncate log inode

Thanks
Changwei

Quoted 96 lines

Thanks,
Larry

Quoted 89 lines · Changwei Ge

Hi Larry,

I'd like to propose another way to fix this issue, which might be easier. :-)
Actually, ocfs2_reserve_clusters() inside will flush truncate log if there is no enough clusters left.
So how about just remove __ocfs2_flush_truncate_log() in ocfs2_defrag_extent()?

Thanks,
Changwei

Quoted 75 lines · Larry Chen

ocfs2_defrag_extent may fall into dead lock.

ocfs2_ioctl_move_extents
???? ocfs2_ioctl_move_extents
?????? ocfs2_move_extents
???????? ocfs2_defrag_extent
?????????? ocfs2_lock_allocators_move_extents

???????????? ocfs2_reserve_clusters ?????????????? inode_lock GLOBAL_BITMAP_SYSTEM_INODE

?????? __ocfs2_flush_truncate_log ?????????????? inode_lock GLOBAL_BITMAP_SYSTEM_INODE

As back trace shows above, ocfs2_reserve_clusters will call inode_lock
against the global bitmap if local allocator has not sufficient cluters.
Once global bitmap could meet the demand, ocfs2_reserve_cluster will return success with global bitmap locked.

After ocfs2_reserve_cluster, if truncate log is full, __ocfs2_flush_truncate_log will definitely fall into dead lock because it needs to inode_lock global bitmap, which has already been locked.

To fix this bug, we could remove from ocfs2_lock_allocators_move_extents
the code which intends to lock global allocator, and put the removed code after __ocfs2_flush_truncate_log

The ocfs2_lock_allocators_move_extents has been refered by 2 places, one
is here, the other does not need the data allocator context, which means
this patch does not affect the caller so far.

---
??? fs/ocfs2/move_extents.c | 13 ++++++------- ??? 1 file changed, 6 insertions(+), 7 deletions(-)

fs/ocfs2/move_extents.c +0 -6
index 7eb3b0a6347e..064dedf40d74 100644
@@ -192,13 +192,6 @@ static int
 ocfs2_lock_allocators_move_extents(struct inode *inode,
 ??????????? goto out;
 ??????? }
 ??? -??? if (data_ac) {
-??????? ret = ocfs2_reserve_clusters(osb, clusters_to_move, 
 data_ac);
-??????? if (ret) {
-??????????? mlog_errno(ret);
-??????????? goto out;
-??????? }
-??? }
 ??? ?????? *credits += ocfs2_calc_extend_credits(osb->sb,

et->et_root_el);
??? @@ -283,6 +276,12 @@ static int ocfs2_defrag_extent(struct ocfs2_move_extents_context *context,
??????????? }
??????? }
??? +??? ret = ocfs2_reserve_clusters(osb, *len, &context->data_ac); +??? if (ret) {
+??????? mlog_errno(ret);
+??????? goto out_unlock_mutex;
+??? }
+
??????? handle = ocfs2_start_trans(osb, credits);
??????? if (IS_ERR(handle)) {
??????????? ret = PTR_ERR(handle);

author
#9 lore raw

PATCH 1/1 fix dead lock caused by ocfs2_defrag_extent

Hi Changwei,

Quoted 14 lines · Changwei Ge

Hi Larry,

Quoted 10 lines · Larry Chen

Hi Changwei,

Maybe we need more investigation.

The following is your fix:
lock truncate log inode
??? __ocfs2_flush_truncate_log()
????ocfs2_lock_allocators_move_extents()
??????? unlock truncate log inode

Quoted 12 lines
Quoted 4 lines

The lock action will happen like following:
lock(truncate_inode)
????lock(sub allocat)
????lock(local_alloc) or lock(global_bitmap)

I don't think we have to worry much about mixed order? of cluster lock and inode mutex since cluster lock granted node will directly succeed instead of waiting for itself.

Quoted 3 lines

I'm not sure if there is another code path that tries to get the same locks but in different order, which may causes dead locks.

Yeah, I use lock to mean both inode_lock and ocfs2_inode_lock. As too many types of lock and inode locks are involved, I can not guarantee that there is no logic error.

Quoted 8 lines
Quoted 6 lines

Indeed ocfs2 involves too many locks, I would like to reduce the deadlock risk at max extent.

Another way is to add an new argument for __ocfs2_flush_truncate which indicates whether global bitmap is needed to be locked.

Sounds a feasible way :)

Haha, I also prefer this way :)
I'll send another patch and run test cases on my environment.

Thanks,
Larry

author
#10 lore raw

PATCH 1/1 fix dead lock caused by ocfs2_defrag_extent

Hi Changwei,

Quoted 6 lines
Quoted 5 lines
Quoted 3 lines

Another way is to add an new argument for __ocfs2_flush_truncate which indicates whether global bitmap is needed to be locked.

Sounds a feasible way :)

I tried this way, but it still causes dead lock. Here is the back trace. The root cause is that ocfs2 sometimes relies on work queue to flush truncate log.

The worker who process the work queue is ocfs2_truncate_log_worker, which directly call ocfs2_flush_truncate_log. And its lock order is truncate_log and then global bitmap, which conflicts with ocfs2_defrag_extent.

#0 [ffffb6c000a03c50] __schedule at ffffffff966c47bf
#1 [ffffb6c000a03ce0] schedule at ffffffff966c4e08
#2 [ffffb6c000a03ce8] rwsem_down_write_failed at ffffffff966c82b3 #3 [ffffb6c000a03d80] schedule at ffffffff966c4e08
#4 [ffffb6c000a03d88] call_rwsem_down_write_failed at ffffffff963a6e03 #5 [ffffb6c000a03dc8] down_write at ffffffff966c7410
try global bit map
#6 [ffffb6c000a03dd0] __ocfs2_flush_truncate_log at ffffffffc0749145
[ocfs2]
got truncate log
#7 [ffffb6c000a03e68] ocfs2_flush_truncate_log at ffffffffc0749afd [ocfs2] #8 [ffffb6c000a03e80] ocfs2_truncate_log_worker at ffffffffc0749b29
[ocfs2]
#9 [ffffb6c000a03e98] process_one_work at ffffffff9609e64a
#10 [ffffb6c000a03ed8] worker_thread at ffffffff9609e88b
#11 [ffffb6c000a03f10] kthread at ffffffff960a470a
#12 [ffffb6c000a03f50] ret_from_fork at ffffffff96800235

[<ffffffff963a6e03>] call_rwsem_down_write_failed+0x13/0x20
         try truncate log
	got global bm
[<ffffffffc07906f3>] ocfs2_defrag_extent+0x1d3/0x680 [ocfs2] 

[<ffffffffc07919f9>] ocfs2_move_extents+0x329/0x740 [ocfs2] 

[<ffffffffc0791f43>] ocfs2_ioctl_move_extents+0x133/0x2d0 [ocfs2] 

[<ffffffffc0773a83>] ocfs2_ioctl+0x253/0x640 [ocfs2] 

[<ffffffff96256480>] do_vfs_ioctl+0x90/0x5f0 

[<ffffffff96256a54>] SyS_ioctl+0x74/0x80 

[<ffffffff96003ae4>] do_syscall_64+0x74/0x140 

[<ffffffff9680009a>] entry_SYSCALL_64_after_hwframe+0x3d/0xa2 

[<ffffffffffffffff>] 0xffffffffffffffff