ocfs2: fix BUG in ocfs2_downconvert_thread_do_work
PATCH 1/1 ocfs2: fix BUG in ocfs2_downconvert_thread_do_work
BUG_ON(list_empty(&osb->blocked_lock_list)) in ocfs2_downconvert_thread_do_work can be triggered in the following case:
ocfs2dc has firstly saved osb->blocked_lock_count to local varibale processed, and then processes the dentry lockres.
During the dentry put, it calls iput and then deletes rw, inode and open lockres from blocked list in ocfs2_mark_lockres_freeing. And this casues the variable processed is not the number of blocked lockres to be processed and then triggers the BUG.
--- fs/ocfs2/dlmglue.c | 10 ++++------ fs/ocfs2/ocfs2.h | 1 + fs/ocfs2/super.c | 1 + 3 files changed, 6 insertions(+), 6 deletions(-)
fs/ocfs2/dlmglue.c +4 -6
@@ -3198,6 +3198,7 @@ void ocfs2_mark_lockres_freeing(struct ocfs2_super *osb,spin_lock_irqsave (&osb->dc_task_lock, flags2);list_del_init (&lockres->l_blocked_list); osb->blocked_lock_count--; + osb->blocked_lock_processed--;spin_unlock_irqrestore (&osb->dc_task_lock, flags2);/* * Warn if we recurse into another post_unlock call. Strictly
@@ -4015,7 +4016,6 @@ static void ocfs2_schedule_blocked_lock(struct ocfs2_super *osb,static void ocfs2_downconvert_thread_do_work (struct ocfs2_super *osb) { -unsigned long processed;unsigned long flags;struct ocfs2_lock_res *lockres;
@@ -4024,19 +4024,17 @@ static void ocfs2_downconvert_thread_do_work(struct ocfs2_super *osb)* wake happens part-way through our work */ osb->dc_work_sequence = osb->dc_wake_sequence; - processed = osb->blocked_lock_count; -while (processed) { + osb->blocked_lock_processed = osb->blocked_lock_count; +while (osb->blocked_lock_processed) {BUG_ON (list_empty (&osb->blocked_lock_list)); lockres =list_entry (osb->blocked_lock_list.next, struct ocfs2_lock_res, l_blocked_list);list_del_init (&lockres->l_blocked_list); osb->blocked_lock_count--; + osb->blocked_lock_processed--;spin_unlock_irqrestore (&osb->dc_task_lock, flags); -BUG_ON (!processed); - processed--; -ocfs2_process_blocked_lock (osb, lockres);spin_lock_irqsave (&osb->dc_task_lock, flags);
fs/ocfs2/ocfs2.h +1 -0
@@ -424,6 +424,7 @@ struct ocfs2_super*/ struct list_head blocked_lock_list;unsigned long blocked_lock_count; +unsigned long blocked_lock_processed;/* List of dquot structures to drop last reference to */ struct llist_head dquot_drop_list;
fs/ocfs2/super.c +1 -0
@@ -2089,6 +2089,7 @@ static int ocfs2_initialize_super(struct super_block *sb, osb->dc_wake_sequence =0 ;INIT_LIST_HEAD (&osb->blocked_lock_list); osb->blocked_lock_count =0 ; + osb->blocked_lock_processed =0 ;spin_lock_init (&osb->osb_lock);spin_lock_init (&osb->osb_xattr_lock);ocfs2_init_steal_slots (osb);
Signature · 1 line
1.8.4.3
PATCH 1/1 ocfs2: fix BUG in ocfs2_downconvert_thread_do_work
Quoted 11 lines · On Wed, 6 May 2015 19:12:54 +0800 Joseph Qi <[email protected]> wrote:
BUG_ON(list_empty(&osb->blocked_lock_list)) in
ocfs2_downconvert_thread_do_workcan be triggered in the following case:
ocfs2dc has firstly savedosb->blocked_lock_countto local varibale processed, and then processes the dentry lockres.
During the dentry put, it calls iput and then deletes rw, inode and open lockres from blocked list inocfs2_mark_lockres_freeing. And this casues the variable processed is not the number of blocked lockres to be processed and then triggers the BUG.Signed-off-by Joseph Qi <[email protected]>Cc <[email protected]>
cc:stable means I'd like to merge this into 4.1-rcx. Can we get some acks, please?
PATCH 1/1 ocfs2: fix BUG in ocfs2_downconvert_thread_do_work
In reply to Joseph QiThanks Joseph,
This seems like a convoluted way of fixing the problem. The reason downconvert_thread_do_work is saving the # of blocked locks is so it doesn't spin forever while another process continually adds them - basically we want to do a certain amount of work and then move on. Instead why not just change the while loop condition (and add a nice comment explaining it):
/*
* blocked lock processing in this loop might call iput which can
* remove items off osb->blocked_lock_list. Downconvert up to
* 'processed' number of locks, but stop short if we had some
* removed by another thread.
*/
while (processed && !list_empty(&osb->blocked_lock_list)) {
...
}As a bonus, we can remove the now-useless BUG_ON().
--Mark
Quoted 87 lines · Joseph Qi
BUG_ON(list_empty(&osb->blocked_lock_list)) in
ocfs2_downconvert_thread_do_workcan be triggered in the following case:
ocfs2dc has firstly savedosb->blocked_lock_countto local varibale processed, and then processes the dentry lockres.
During the dentry put, it calls iput and then deletes rw, inode and open lockres from blocked list inocfs2_mark_lockres_freeing. And this casues the variable processed is not the number of blocked lockres to be processed and then triggers the BUG.Signed-off-by Joseph Qi <[email protected]>Cc <[email protected]>--- fs/ocfs2/dlmglue.c | 10 ++++------ fs/ocfs2/ocfs2.h | 1 + fs/ocfs2/super.c | 1 + 3 files changed, 6 insertions(+), 6 deletions(-)fs/ocfs2/dlmglue.c +4
-6@@ -3198,6 +3198,7 @@ void ocfs2_mark_lockres_freeing(struct ocfs2_super *osb,spin_lock_irqsave (&osb->dc_task_lock, flags2);list_del_init (&lockres->l_blocked_list); osb->blocked_lock_count--; + osb->blocked_lock_processed--;spin_unlock_irqrestore (&osb->dc_task_lock, flags2);/* * Warn if we recurse into another post_unlock call. Strictly @@ -4015,7 +4016,6 @@ static void ocfs2_schedule_blocked_lock(struct ocfs2_super *osb,static void ocfs2_downconvert_thread_do_work (struct ocfs2_super *osb) { -unsigned long processed;unsigned long flags;struct ocfs2_lock_res *lockres;@@ -4024,19 +4024,17 @@ static void ocfs2_downconvert_thread_do_work(struct ocfs2_super *osb)* wake happens part-way through our work */ osb->dc_work_sequence = osb->dc_wake_sequence; - processed = osb->blocked_lock_count; -while (processed) { + osb->blocked_lock_processed = osb->blocked_lock_count; +while (osb->blocked_lock_processed) {BUG_ON (list_empty (&osb->blocked_lock_list)); lockres =list_entry (osb->blocked_lock_list.next, struct ocfs2_lock_res, l_blocked_list);list_del_init (&lockres->l_blocked_list); osb->blocked_lock_count--; + osb->blocked_lock_processed--;spin_unlock_irqrestore (&osb->dc_task_lock, flags); -BUG_ON (!processed); - processed--; -ocfs2_process_blocked_lock (osb, lockres);spin_lock_irqsave (&osb->dc_task_lock, flags);fs/ocfs2/ocfs2.h +1
-0@@ -424,6 +424,7 @@ struct ocfs2_super*/ struct list_head blocked_lock_list;unsigned long blocked_lock_count; +unsigned long blocked_lock_processed;/* List of dquot structures to drop last reference to */ struct llist_head dquot_drop_list;fs/ocfs2/super.c +1
-0@@ -2089,6 +2089,7 @@ static int ocfs2_initialize_super(struct super_block *sb, osb->dc_wake_sequence =0 ;INIT_LIST_HEAD (&osb->blocked_lock_list); osb->blocked_lock_count =0 ; + osb->blocked_lock_processed =0 ;spin_lock_init (&osb->osb_lock);spin_lock_init (&osb->osb_xattr_lock);ocfs2_init_steal_slots (osb);Signature · 1 line
1.8.4.3
--
Mark Fasheh
PATCH 1/1 ocfs2: fix BUG in ocfs2_downconvert_thread_do_work
Hi Mark,
Quoted 18 lines · Mark Fasheh
Thanks Joseph,
This seems like a convoluted way of fixing the problem. The reason
downconvert_thread_do_workis saving the # of blocked locks is so it doesn't spin forever while another process continually adds them - basically we want to do a certain amount of work and then move on. Instead why not just change the while loop condition (and add a nice comment explaining it):/* * blocked lock processing in this loop might call iput which can * remove items off osb->blocked_lock_list. Downconvert up to * 'processed' number of locks, but stop short if we had some * removed by another thread. */ while (processed && !list_empty(&osb->blocked_lock_list)) { ... }
Quoted 3 lines
As a bonus, we can remove the now-useless
BUG_ON().--Mark
Thanks for your advice.
Introducing the blocked_lock_processed is trying to retain the batch processing logic. And I only synchronize it on decreasing, but not adding. If we use !list_empty(&osb->blocked_lock_list) as the loop condition, once the list is corrupted, we may also treat it as normal.
BTW, in the case described, the lockres is removed from the blocked list by the same downconvert thread.
Quoted 93 lines · Joseph Qi
Quoted 87 lines
BUG_ON(list_empty(&osb->blocked_lock_list)) in
ocfs2_downconvert_thread_do_workcan be triggered in the following case:
ocfs2dc has firstly savedosb->blocked_lock_countto local varibale processed, and then processes the dentry lockres.
During the dentry put, it calls iput and then deletes rw, inode and open lockres from blocked list inocfs2_mark_lockres_freeing. And this casues the variable processed is not the number of blocked lockres to be processed and then triggers the BUG.Signed-off-by Joseph Qi <[email protected]>Cc <[email protected]>--- fs/ocfs2/dlmglue.c | 10 ++++------ fs/ocfs2/ocfs2.h | 1 + fs/ocfs2/super.c | 1 + 3 files changed, 6 insertions(+), 6 deletions(-)fs/ocfs2/dlmglue.c +4
-6@@ -3198,6 +3198,7 @@ void ocfs2_mark_lockres_freeing(struct ocfs2_super *osb,spin_lock_irqsave (&osb->dc_task_lock, flags2);list_del_init (&lockres->l_blocked_list); osb->blocked_lock_count--; + osb->blocked_lock_processed--;spin_unlock_irqrestore (&osb->dc_task_lock, flags2);/* * Warn if we recurse into another post_unlock call. Strictly @@ -4015,7 +4016,6 @@ static void ocfs2_schedule_blocked_lock(struct ocfs2_super *osb,static void ocfs2_downconvert_thread_do_work (struct ocfs2_super *osb) { -unsigned long processed;unsigned long flags;struct ocfs2_lock_res *lockres;@@ -4024,19 +4024,17 @@ static void ocfs2_downconvert_thread_do_work(struct ocfs2_super *osb)* wake happens part-way through our work */ osb->dc_work_sequence = osb->dc_wake_sequence; - processed = osb->blocked_lock_count; -while (processed) { + osb->blocked_lock_processed = osb->blocked_lock_count; +while (osb->blocked_lock_processed) {BUG_ON (list_empty (&osb->blocked_lock_list)); lockres =list_entry (osb->blocked_lock_list.next, struct ocfs2_lock_res, l_blocked_list);list_del_init (&lockres->l_blocked_list); osb->blocked_lock_count--; + osb->blocked_lock_processed--;spin_unlock_irqrestore (&osb->dc_task_lock, flags); -BUG_ON (!processed); - processed--; -ocfs2_process_blocked_lock (osb, lockres);spin_lock_irqsave (&osb->dc_task_lock, flags);fs/ocfs2/ocfs2.h +1
-0@@ -424,6 +424,7 @@ struct ocfs2_super*/ struct list_head blocked_lock_list;unsigned long blocked_lock_count; +unsigned long blocked_lock_processed;/* List of dquot structures to drop last reference to */ struct llist_head dquot_drop_list;fs/ocfs2/super.c +1
-0@@ -2089,6 +2089,7 @@ static int ocfs2_initialize_super(struct super_block *sb, osb->dc_wake_sequence =0 ;INIT_LIST_HEAD (&osb->blocked_lock_list); osb->blocked_lock_count =0 ; + osb->blocked_lock_processed =0 ;spin_lock_init (&osb->osb_lock);spin_lock_init (&osb->osb_xattr_lock);ocfs2_init_steal_slots (osb);Signature · 1 line
1.8.4.3--
Mark Fasheh.