ocfs2: use spinlock irqsave for downconvert lock.patch
PATCH 1/1 ocfs2: use spinlock irqsave for downconvert lock.patch
When ocfs2dc thread holds dc_task_lock spinlock and receives soft IRQ for I/O completion it deadlock itself trying to get same spinlock in ocfs2_wake_downconvert_thread
The patch disables interrupts when acquiring dc_task_lock spinlock
--- fs/ocfs2/dlmglue.c | 30 ++++++++++++++++++------------ 1 files changed, 18 insertions(+), 12 deletions(-)
fs/ocfs2/dlmglue.c +18 -12
@@ -3932,6 +3932,8 @@ unqueue:static void ocfs2_schedule_blocked_lock (struct ocfs2_super *osb,struct ocfs2_lock_res *lockres) { +unsigned long flags; +assert_spin_locked (&lockres->l_lock);if (lockres->l_flags &OCFS2_LOCK_FREEING ) {
@@ -3945,21 +3947,22 @@ static void ocfs2_schedule_blocked_lock(struct ocfs2_super *osb,lockres_or_flags (lockres,OCFS2_LOCK_QUEUED ); -spin_lock (&osb->dc_task_lock); +spin_lock_irqsave (&osb->dc_task_lock, flags);if (list_empty (&lockres->l_blocked_list)) {list_add_tail (&lockres->l_blocked_list, &osb->blocked_lock_list); osb->blocked_lock_count++; } -spin_unlock (&osb->dc_task_lock); +spin_unlock_irqrestore (&osb->dc_task_lock, flags); }static void ocfs2_downconvert_thread_do_work (struct ocfs2_super *osb) {unsigned long processed; +unsigned long flags;struct ocfs2_lock_res *lockres; -spin_lock (&osb->dc_task_lock); +spin_lock_irqsave (&osb->dc_task_lock, flags);/* grab this early so we know to try again if a state change and * wake happens part-way through our work */ osb->dc_work_sequence = osb->dc_wake_sequence;
@@ -3972,38 +3975,40 @@ static void ocfs2_downconvert_thread_do_work(struct ocfs2_super *osb) struct ocfs2_lock_res, l_blocked_list);list_del_init (&lockres->l_blocked_list); osb->blocked_lock_count--; -spin_unlock (&osb->dc_task_lock); +spin_unlock_irqrestore (&osb->dc_task_lock, flags);BUG_ON (!processed); processed--;ocfs2_process_blocked_lock (osb, lockres); -spin_lock (&osb->dc_task_lock); +spin_lock_irqsave (&osb->dc_task_lock, flags); } -spin_unlock (&osb->dc_task_lock); +spin_unlock_irqrestore (&osb->dc_task_lock, flags); }static int ocfs2_downconvert_thread_lists_empty (struct ocfs2_super *osb) {int empty =0 ; +unsigned long flags; -spin_lock (&osb->dc_task_lock); +spin_lock_irqsave (&osb->dc_task_lock, flags);if (list_empty (&osb->blocked_lock_list)) empty =1 ; -spin_unlock (&osb->dc_task_lock); +spin_unlock_irqrestore (&osb->dc_task_lock, flags);return empty; }static int ocfs2_downconvert_thread_should_wake (struct ocfs2_super *osb) {int should_wake =0 ; +unsigned long flags; -spin_lock (&osb->dc_task_lock); +spin_lock_irqsave (&osb->dc_task_lock, flags);if (osb->dc_work_sequence != osb->dc_wake_sequence) should_wake =1 ; -spin_unlock (&osb->dc_task_lock); +spin_unlock_irqrestore (&osb->dc_task_lock, flags);return should_wake; }
@@ -4033,10 +4038,11 @@ static int ocfs2_downconvert_thread(void *arg)void ocfs2_wake_downconvert_thread (struct ocfs2_super *osb) { -spin_lock (&osb->dc_task_lock); +unsigned long flags; +spin_lock_irqsave (&osb->dc_task_lock, flags);/* make sure the voting thread gets a swipe at whatever changes * the caller may have made to the voting state */ osb->dc_wake_sequence++; -spin_unlock (&osb->dc_task_lock); +spin_unlock_irqrestore (&osb->dc_task_lock, flags);wake_up (&osb->dc_event); }
Signature · 1 line
1.5.4.3
PATCH 1/1 ocfs2: use spinlock irqsave for downconvert lock.patch
Hi Srini,
Quoted 3 lines · Srinivas Eeda
When ocfs2dc thread holds
dc_task_lockspinlock and receives soft IRQ for I/O completion it deadlock itself trying to get same spinlock inocfs2_wake_downconvert_thread
could you please describe it in more detail?
btw, if you are afraid of deadlocking in soft IRQ, I guess we should use spin_lock_bh not spin_lock_irqsave?
Thanks
Tao
Quoted 109 lines
The patch disables interrupts when acquiring
dc_task_lockspinlockSigned-off-by Srinivas Eeda <[email protected]>--- fs/ocfs2/dlmglue.c | 30 ++++++++++++++++++------------ 1 files changed, 18 insertions(+), 12 deletions(-)fs/ocfs2/dlmglue.c +18
-12@@ -3932,6 +3932,8 @@ unqueue:static void ocfs2_schedule_blocked_lock (struct ocfs2_super *osb,struct ocfs2_lock_res *lockres) { +unsigned long flags; +assert_spin_locked (&lockres->l_lock);if (lockres->l_flags &OCFS2_LOCK_FREEING ) {@@ -3945,21 +3947,22 @@ static void ocfs2_schedule_blocked_lock(struct ocfs2_super *osb,lockres_or_flags (lockres,OCFS2_LOCK_QUEUED ); -spin_lock (&osb->dc_task_lock); +spin_lock_irqsave (&osb->dc_task_lock, flags);if (list_empty (&lockres->l_blocked_list)) {list_add_tail (&lockres->l_blocked_list, &osb->blocked_lock_list); osb->blocked_lock_count++; } -spin_unlock (&osb->dc_task_lock); +spin_unlock_irqrestore (&osb->dc_task_lock, flags); }static void ocfs2_downconvert_thread_do_work (struct ocfs2_super *osb) {unsigned long processed; +unsigned long flags;struct ocfs2_lock_res *lockres; -spin_lock (&osb->dc_task_lock); +spin_lock_irqsave (&osb->dc_task_lock, flags);/* grab this early so we know to try again if a state change and * wake happens part-way through our work */ osb->dc_work_sequence = osb->dc_wake_sequence;@@ -3972,38 +3975,40 @@ static void ocfs2_downconvert_thread_do_work(struct ocfs2_super *osb) struct ocfs2_lock_res, l_blocked_list);list_del_init (&lockres->l_blocked_list); osb->blocked_lock_count--; -spin_unlock (&osb->dc_task_lock); +spin_unlock_irqrestore (&osb->dc_task_lock, flags);BUG_ON (!processed); processed--;ocfs2_process_blocked_lock (osb, lockres); -spin_lock (&osb->dc_task_lock); +spin_lock_irqsave (&osb->dc_task_lock, flags); } -spin_unlock (&osb->dc_task_lock); +spin_unlock_irqrestore (&osb->dc_task_lock, flags); }static int ocfs2_downconvert_thread_lists_empty (struct ocfs2_super *osb) {int empty =0 ; +unsigned long flags; -spin_lock (&osb->dc_task_lock); +spin_lock_irqsave (&osb->dc_task_lock, flags);if (list_empty (&osb->blocked_lock_list)) empty =1 ; -spin_unlock (&osb->dc_task_lock); +spin_unlock_irqrestore (&osb->dc_task_lock, flags);return empty; }static int ocfs2_downconvert_thread_should_wake (struct ocfs2_super *osb) {int should_wake =0 ; +unsigned long flags; -spin_lock (&osb->dc_task_lock); +spin_lock_irqsave (&osb->dc_task_lock, flags);if (osb->dc_work_sequence != osb->dc_wake_sequence) should_wake =1 ; -spin_unlock (&osb->dc_task_lock); +spin_unlock_irqrestore (&osb->dc_task_lock, flags);return should_wake; }@@ -4033,10 +4038,11 @@ static int ocfs2_downconvert_thread(void *arg)void ocfs2_wake_downconvert_thread (struct ocfs2_super *osb) { -spin_lock (&osb->dc_task_lock); +unsigned long flags; +spin_lock_irqsave (&osb->dc_task_lock, flags);/* make sure the voting thread gets a swipe at whatever changes * the caller may have made to the voting state */ osb->dc_wake_sequence++; -spin_unlock (&osb->dc_task_lock); +spin_unlock_irqrestore (&osb->dc_task_lock, flags);wake_up (&osb->dc_event); }
PATCH 1/1 ocfs2: use spinlock irqsave for downconvert lock.patch
Hi Tao,
thanks for reviewing.
Quoted 4 lines
Quoted 3 lines
When ocfs2dc thread holds
dc_task_lockspinlock and receives soft IRQ for I/O completion it deadlock itself trying to get same spinlock inocfs2_wake_downconvert_threadcould you please describe it in more detail?
When ocfs2dc thread is running on a cpu and acquired dc_task_lock spin lock, an interrupt came in for i/o completion. Interrupt handler then tries to acquire same spinlock and ends up in deadlock. Below is the stack that gives more details.
[<ffffffff814fd509>] default_do_nmi+0x39/0x200 [<ffffffff814fd750>] do_nmi+0x80/0xa0 [<ffffffff814fced0>] nmi+0x20/0x30 [<ffffffff8103ee83>] ? __ticket_spin_lock+0x13/0x20 <<EOE>> <IRQ> [<ffffffff814fc41e>] _raw_spin_lock+0xe/0x20 [<ffffffffa04e32e8>] ocfs2_wake_downconvert_thread+0x28/0x60 [ocfs2] [<ffffffffa04eb388>] ocfs2_rw_unlock+0xe8/0x1a0 [ocfs2] [<ffffffff8110c635>] ? mempool_free+0x95/0xa0 [<ffffffffa04d18c1>] ocfs2_dio_end_io+0x61/0xb0 [ocfs2] [<ffffffff8119ebdb>] dio_complete+0xbb/0xe0 [<ffffffff8119ec6d>] dio_bio_end_aio+0x6d/0xc0 [<ffffffff8119a1bd>] bio_endio+0x1d/0x40 [<ffffffff81234643>] req_bio_endio+0xa3/0xe0 [<ffffffff8123589f>] blk_update_request+0xff/0x490 [<ffffffff8119bec4>] ? bio_free+0x64/0x70 [<ffffffffa000193a>] end_clone_bio+0x5a/0x90 [dm_mod] [<ffffffff8119a1bd>] bio_endio+0x1d/0x40 [<ffffffff81234643>] req_bio_endio+0xa3/0xe0 [<ffffffff813558a6>] ? scsi_done+0x26/0x60 [<ffffffff8123589f>] blk_update_request+0xff/0x490 [<ffffffffa01ee77a>] ? qla2x00_process_completed_request+0x5a/0xe0 [qla2xxx] [<ffffffff81235c57>] blk_update_bidi_request+0x27/0xb0 [<ffffffff81236d8f>] blk_end_bidi_request+0x2f/0x80 [<ffffffff81236e30>] blk_end_request+0x10/0x20 [<ffffffff8135d600>] scsi_end_request+0x40/0xb0 [<ffffffff8135d96f>] scsi_io_completion+0x9f/0x5c0 [<ffffffff8103ef19>] ? default_spin_lock_flags+0x9/0x10 [<ffffffff81354a59>] scsi_finish_command+0xc9/0x130 [<ffffffff8135dff7>] scsi_softirq_done+0x147/0x170 [<ffffffff8123ca32>] blk_done_softirq+0x82/0xa0 [<ffffffff8106f987>] __do_softirq+0xb7/0x210 [<ffffffff814fc41e>] ? _raw_spin_lock+0xe/0x20 [<ffffffff8150599c>] call_softirq+0x1c/0x30 [<ffffffff81016365>] do_softirq+0x65/0xa0 [<ffffffff8106f78d>] irq_exit+0xbd/0xe0 [<ffffffff815061f6>] do_IRQ+0x66/0xe0 @ [<ffffffff814fc953>] common_interrupt+0x13/0x13 <EOI> [<ffffffff81261625>] ? __list_del_entry+0x35/0xd0 [<ffffffffa04e9d53>] ocfs2_downconvert_thread+0x133/0x2a0 [ocfs2] [<ffffffff814f9d10>] ? __schedule+0x3f0/0x800 [<ffffffff8108b930>] ? wake_up_bit+0x40/0x40 [<ffffffffa04e9c20>] ? ocfs2_process_blocked_lock+0x270/0x270 [ocfs2] [<ffffffff8108b346>] kthread+0x96/0xa0 [<ffffffff815058a4>] kernel_thread_helper+0x4/0x10 [<ffffffff8108b2b0>] ? kthread_worker_fn+0x1a0/0x1a0 [<ffffffff815058a0>] ? gs_change+0x13/0x13
@ ---[ end trace 628bf423ee0bc8a8 ]---
Quoted 2 lines
btw, if you are afraid of deadlocking in soft IRQ, I guess we should use
spin_lock_bhnotspin_lock_irqsave?
Yes, looks like we could, but I am not 100% sure if it will be safe?
Quoted 110 lines
Thanks
TaoQuoted 108 lines
The patch disables interrupts when acquiring
dc_task_lockspinlockSigned-off-by Srinivas Eeda<[email protected]>--- fs/ocfs2/dlmglue.c | 30 ++++++++++++++++++------------ 1 files changed, 18 insertions(+), 12 deletions(-)fs/ocfs2/dlmglue.c +18
-12@@ -3932,6 +3932,8 @@ unqueue:static void ocfs2_schedule_blocked_lock (struct ocfs2_super *osb,struct ocfs2_lock_res *lockres) { +unsigned long flags; +assert_spin_locked (&lockres->l_lock);if (lockres->l_flags&OCFS2_LOCK_FREEING ) {@@ -3945,21 +3947,22 @@ static void ocfs2_schedule_blocked_lock(struct ocfs2_super *osb,lockres_or_flags (lockres,OCFS2_LOCK_QUEUED ); -spin_lock (&osb->dc_task_lock); +spin_lock_irqsave (&osb->dc_task_lock, flags);if (list_empty (&lockres->l_blocked_list)) {list_add_tail (&lockres->l_blocked_list, &osb->blocked_lock_list); osb->blocked_lock_count++; } -spin_unlock (&osb->dc_task_lock); +spin_unlock_irqrestore (&osb->dc_task_lock, flags); }static void ocfs2_downconvert_thread_do_work (struct ocfs2_super *osb) {unsigned long processed; +unsigned long flags;struct ocfs2_lock_res *lockres; -spin_lock (&osb->dc_task_lock); +spin_lock_irqsave (&osb->dc_task_lock, flags);/* grab this early so we know to try again if a state change and * wake happens part-way through our work */ osb->dc_work_sequence = osb->dc_wake_sequence;@@ -3972,38 +3975,40 @@ static void ocfs2_downconvert_thread_do_work(struct ocfs2_super *osb) struct ocfs2_lock_res, l_blocked_list);list_del_init (&lockres->l_blocked_list); osb->blocked_lock_count--; -spin_unlock (&osb->dc_task_lock); +spin_unlock_irqrestore (&osb->dc_task_lock, flags);BUG_ON (!processed); processed--;ocfs2_process_blocked_lock (osb, lockres); -spin_lock (&osb->dc_task_lock); +spin_lock_irqsave (&osb->dc_task_lock, flags); } -spin_unlock (&osb->dc_task_lock); +spin_unlock_irqrestore (&osb->dc_task_lock, flags); }static int ocfs2_downconvert_thread_lists_empty (struct ocfs2_super *osb) {int empty =0 ; +unsigned long flags; -spin_lock (&osb->dc_task_lock); +spin_lock_irqsave (&osb->dc_task_lock, flags);if (list_empty (&osb->blocked_lock_list)) empty =1 ; -spin_unlock (&osb->dc_task_lock); +spin_unlock_irqrestore (&osb->dc_task_lock, flags);return empty; }static int ocfs2_downconvert_thread_should_wake (struct ocfs2_super *osb) {int should_wake =0 ; +unsigned long flags; -spin_lock (&osb->dc_task_lock); +spin_lock_irqsave (&osb->dc_task_lock, flags);if (osb->dc_work_sequence != osb->dc_wake_sequence) should_wake =1 ; -spin_unlock (&osb->dc_task_lock); +spin_unlock_irqrestore (&osb->dc_task_lock, flags);return should_wake; }@@ -4033,10 +4038,11 @@ static int ocfs2_downconvert_thread(void *arg)void ocfs2_wake_downconvert_thread (struct ocfs2_super *osb) { -spin_lock (&osb->dc_task_lock); +unsigned long flags; +spin_lock_irqsave (&osb->dc_task_lock, flags);/* make sure the voting thread gets a swipe at whatever changes * the caller may have made to the voting state */ osb->dc_wake_sequence++; -spin_unlock (&osb->dc_task_lock); +spin_unlock_irqrestore (&osb->dc_task_lock, flags);wake_up (&osb->dc_event); }
PATCH 1/1 ocfs2: use spinlock irqsave for downconvert lock.patch
Quoted 60 lines · srinivas eeda
Hi Tao,
thanks for reviewing.Quoted 5 lines
Quoted 4 lines
When ocfs2dc thread holds
dc_task_lockspinlock and receives soft IRQ for
I/O completion it deadlock itself trying to get same spinlock inocfs2_wake_downconvert_threadcould you please describe it in more detail?
When ocfs2dc thread is running on a cpu and acquired
dc_task_lockspin lock, an interrupt came in for i/o completion. Interrupt handler then tries to acquire same spinlock and ends up in deadlock. Below is the stack that gives more details.[<ffffffff814fd509>] default_do_nmi+0x39/0x200 [<ffffffff814fd750>] do_nmi+0x80/0xa0 [<ffffffff814fced0>] nmi+0x20/0x30 [<ffffffff8103ee83>] ? __ticket_spin_lock+0x13/0x20 <<EOE>> <IRQ> [<ffffffff814fc41e>] _raw_spin_lock+0xe/0x20 [<ffffffffa04e32e8>] ocfs2_wake_downconvert_thread+0x28/0x60 [ocfs2] [<ffffffffa04eb388>] ocfs2_rw_unlock+0xe8/0x1a0 [ocfs2] [<ffffffff8110c635>] ? mempool_free+0x95/0xa0 [<ffffffffa04d18c1>] ocfs2_dio_end_io+0x61/0xb0 [ocfs2] [<ffffffff8119ebdb>] dio_complete+0xbb/0xe0 [<ffffffff8119ec6d>] dio_bio_end_aio+0x6d/0xc0 [<ffffffff8119a1bd>] bio_endio+0x1d/0x40 [<ffffffff81234643>] req_bio_endio+0xa3/0xe0 [<ffffffff8123589f>] blk_update_request+0xff/0x490 [<ffffffff8119bec4>] ? bio_free+0x64/0x70 [<ffffffffa000193a>] end_clone_bio+0x5a/0x90 [dm_mod] [<ffffffff8119a1bd>] bio_endio+0x1d/0x40 [<ffffffff81234643>] req_bio_endio+0xa3/0xe0 [<ffffffff813558a6>] ? scsi_done+0x26/0x60 [<ffffffff8123589f>] blk_update_request+0xff/0x490 [<ffffffffa01ee77a>] ? qla2x00_process_completed_request+0x5a/0xe0 [qla2xxx] [<ffffffff81235c57>] blk_update_bidi_request+0x27/0xb0 [<ffffffff81236d8f>] blk_end_bidi_request+0x2f/0x80 [<ffffffff81236e30>] blk_end_request+0x10/0x20 [<ffffffff8135d600>] scsi_end_request+0x40/0xb0 [<ffffffff8135d96f>] scsi_io_completion+0x9f/0x5c0 [<ffffffff8103ef19>] ? default_spin_lock_flags+0x9/0x10 [<ffffffff81354a59>] scsi_finish_command+0xc9/0x130 [<ffffffff8135dff7>] scsi_softirq_done+0x147/0x170 [<ffffffff8123ca32>] blk_done_softirq+0x82/0xa0 [<ffffffff8106f987>] __do_softirq+0xb7/0x210 [<ffffffff814fc41e>] ? _raw_spin_lock+0xe/0x20 [<ffffffff8150599c>] call_softirq+0x1c/0x30 [<ffffffff81016365>] do_softirq+0x65/0xa0 [<ffffffff8106f78d>] irq_exit+0xbd/0xe0 [<ffffffff815061f6>] do_IRQ+0x66/0xe0 @ [<ffffffff814fc953>] common_interrupt+0x13/0x13 <EOI> [<ffffffff81261625>] ? __list_del_entry+0x35/0xd0 [<ffffffffa04e9d53>] ocfs2_downconvert_thread+0x133/0x2a0 [ocfs2] [<ffffffff814f9d10>] ? __schedule+0x3f0/0x800 [<ffffffff8108b930>] ? wake_up_bit+0x40/0x40 [<ffffffffa04e9c20>] ? ocfs2_process_blocked_lock+0x270/0x270 [ocfs2] [<ffffffff8108b346>] kthread+0x96/0xa0 [<ffffffff815058a4>] kernel_thread_helper+0x4/0x10 [<ffffffff8108b2b0>] ? kthread_worker_fn+0x1a0/0x1a0 [<ffffffff815058a0>] ? gs_change+0x13/0x13
Quoted 1 line
@ ---[ end trace
628bf423ee0bc8a8]---
Thanks for the perfect stack. :)
Quoted 3 lines
Quoted 2 lines
btw, if you are afraid of deadlocking in soft IRQ, I guess we should use
spin_lock_bhnotspin_lock_irqsave?Yes, looks like we could, but I am not 100% sure if it will be safe?
oh, I just went through the whole path and it sees that we also call spin_lock_irqsave in __ocfs2_cluster_unlock, but can we ever use lock->l_lock in a hard irq context?
Mark and Joel,
do you ever know whey we need this irqsave lock?
anyway, in a softirq context, it should be safe to use a spin_lock_irqsave, so the code itself should be fine.
Thanks
Tao
Quoted 114 lines
Quoted 114 lines
Thanks
TaoQuoted 112 lines
The patch disables interrupts when acquiring
dc_task_lockspinlockSigned-off-by Srinivas Eeda<[email protected]>--- fs/ocfs2/dlmglue.c | 30 ++++++++++++++++++------------ 1 files changed, 18 insertions(+), 12 deletions(-)fs/ocfs2/dlmglue.c +6
-3@@ -3932,6 +3932,8 @@ unqueue:static void ocfs2_schedule_blocked_lock (struct ocfs2_super *osb,struct ocfs2_lock_res *lockres) { +unsigned long flags; +assert_spin_locked (&lockres->l_lock);if (lockres->l_flags&OCFS2_LOCK_FREEING ) {@@ -3945,21 +3947,22 @@ static void ocfs2_schedule_blocked_lock(struct ocfs2_super *osb,lockres_or_flags (lockres, OCFS2_LOCK_QUEUED); -spin_lock (&osb->dc_task_lock); +spin_lock_irqsave (&osb->dc_task_lock, flags);if (list_empty (&lockres->l_blocked_list)) {list_add_tail (&lockres->l_blocked_list, &osb->blocked_lock_list); osb->blocked_lock_count++; } -spin_unlock (&osb->dc_task_lock); +spin_unlock_irqrestore (&osb->dc_task_lock, flags); }static void ocfs2_downconvert_thread_do_work (struct ocfs2_super *osb) {unsigned long processed; +unsigned long flags;struct ocfs2_lock_res *lockres; -spin_lock (&osb->dc_task_lock); +spin_lock_irqsave (&osb->dc_task_lock, flags);/* grab this early so we know to try again if a state change and * wake happens part-way through our work */ osb->dc_work_sequence = osb->dc_wake_sequence;fs/ocfs2/dlmglue.c +9
-7@@ -3972,38 +3975,40 @@ static void ocfs2_downconvert_thread_do_work(struct ocfs2_super *osb) struct ocfs2_lock_res, l_blocked_list);list_del_init (&lockres->l_blocked_list); osb->blocked_lock_count--; -spin_unlock (&osb->dc_task_lock); +spin_unlock_irqrestore (&osb->dc_task_lock, flags);BUG_ON (!processed); processed--;ocfs2_process_blocked_lock (osb, lockres); -spin_lock (&osb->dc_task_lock); +spin_lock_irqsave (&osb->dc_task_lock, flags); } -spin_unlock (&osb->dc_task_lock); +spin_unlock_irqrestore (&osb->dc_task_lock, flags); }static int ocfs2_downconvert_thread_lists_empty (struct ocfs2_super *osb) {int empty =0 ; +unsigned long flags; -spin_lock (&osb->dc_task_lock); +spin_lock_irqsave (&osb->dc_task_lock, flags);if (list_empty (&osb->blocked_lock_list)) empty =1 ; -spin_unlock (&osb->dc_task_lock); +spin_unlock_irqrestore (&osb->dc_task_lock, flags);return empty; }static int ocfs2_downconvert_thread_should_wake (struct ocfs2_super *osb) {int should_wake =0 ; +unsigned long flags; -spin_lock (&osb->dc_task_lock); +spin_lock_irqsave (&osb->dc_task_lock, flags);if (osb->dc_work_sequence != osb->dc_wake_sequence) should_wake =1 ; -spin_unlock (&osb->dc_task_lock); +spin_unlock_irqrestore (&osb->dc_task_lock, flags);return should_wake;}
fs/ocfs2/dlmglue.c +3
-2@@ -4033,10 +4038,11 @@ static int ocfs2_downconvert_thread(void *arg)void ocfs2_wake_downconvert_thread (struct ocfs2_super *osb) { -spin_lock (&osb->dc_task_lock); +unsigned long flags; +spin_lock_irqsave (&osb->dc_task_lock, flags);/* make sure the voting thread gets a swipe at whatever changes * the caller may have made to the voting state */ osb->dc_wake_sequence++; -spin_unlock (&osb->dc_task_lock); +spin_unlock_irqrestore (&osb->dc_task_lock, flags);wake_up (&osb->dc_event); }
PATCH 1/1 ocfs2: use spinlock irqsave for downconvert lock.patch
In reply to Srinivas EedaComments inlined.
Quoted 5 lines · Srinivas Eeda
When ocfs2dc thread holds
dc_task_lockspinlock and receives soft IRQ for I/O completion it deadlock itself trying to get same spinlock inocfs2_wake_downconvert_threadThe patch disables interrupts when acquiring
dc_task_lockspinlock
Maybe add a condensed stack to the description.
ocfs2_downconvert_thread() => do_irq() => do_softirq() => .. => scsi_io_completion() => .. => bio_endio() => .. => ocfs2_dio_end_io() => ocfs2_rw_unlock() => ocfs2_wake_downconvert_thread()
Also, don't be afraid of full stops. ;)
Quoted 94 lines
Signed-off-by Srinivas Eeda<[email protected]>--- fs/ocfs2/dlmglue.c | 30 ++++++++++++++++++------------ 1 files changed, 18 insertions(+), 12 deletions(-)fs/ocfs2/dlmglue.c +15
-10@@ -3932,6 +3932,8 @@ unqueue:static void ocfs2_schedule_blocked_lock (struct ocfs2_super *osb,struct ocfs2_lock_res *lockres) { +unsigned long flags; +assert_spin_locked (&lockres->l_lock);if (lockres->l_flags&OCFS2_LOCK_FREEING ) {@@ -3945,21 +3947,22 @@ static void ocfs2_schedule_blocked_lock(struct ocfs2_super *osb,lockres_or_flags (lockres,OCFS2_LOCK_QUEUED ); -spin_lock (&osb->dc_task_lock); +spin_lock_irqsave (&osb->dc_task_lock, flags);if (list_empty (&lockres->l_blocked_list)) {list_add_tail (&lockres->l_blocked_list, &osb->blocked_lock_list); osb->blocked_lock_count++; } -spin_unlock (&osb->dc_task_lock); +spin_unlock_irqrestore (&osb->dc_task_lock, flags); }static void ocfs2_downconvert_thread_do_work (struct ocfs2_super *osb) {unsigned long processed; +unsigned long flags;struct ocfs2_lock_res *lockres; -spin_lock (&osb->dc_task_lock); +spin_lock_irqsave (&osb->dc_task_lock, flags);/* grab this early so we know to try again if a state change and * wake happens part-way through our work */ osb->dc_work_sequence = osb->dc_wake_sequence;@@ -3972,38 +3975,40 @@ static void ocfs2_downconvert_thread_do_work(struct ocfs2_super *osb) struct ocfs2_lock_res, l_blocked_list);list_del_init (&lockres->l_blocked_list); osb->blocked_lock_count--; -spin_unlock (&osb->dc_task_lock); +spin_unlock_irqrestore (&osb->dc_task_lock, flags);BUG_ON (!processed); processed--;ocfs2_process_blocked_lock (osb, lockres); -spin_lock (&osb->dc_task_lock); +spin_lock_irqsave (&osb->dc_task_lock, flags); } -spin_unlock (&osb->dc_task_lock); +spin_unlock_irqrestore (&osb->dc_task_lock, flags); }static int ocfs2_downconvert_thread_lists_empty (struct ocfs2_super *osb) {int empty =0 ; +unsigned long flags; -spin_lock (&osb->dc_task_lock); +spin_lock_irqsave (&osb->dc_task_lock, flags);if (list_empty (&osb->blocked_lock_list)) empty =1 ; -spin_unlock (&osb->dc_task_lock); +spin_unlock_irqrestore (&osb->dc_task_lock, flags);return empty; }static int ocfs2_downconvert_thread_should_wake (struct ocfs2_super *osb) {int should_wake =0 ; +unsigned long flags; -spin_lock (&osb->dc_task_lock); +spin_lock_irqsave (&osb->dc_task_lock, flags);if (osb->dc_work_sequence != osb->dc_wake_sequence) should_wake =1 ; -spin_unlock (&osb->dc_task_lock); +spin_unlock_irqrestore (&osb->dc_task_lock, flags);return should_wake; }
fs/ocfs2/dlmglue.c static int ocfs2_downconvert_thread(void *arg) · line 4041
Quoted 5 lines
fs/ocfs2/dlmglue.c +1
-1@@ -4033,10 +4038,11 @@ static int ocfs2_downconvert_thread(void *arg)void ocfs2_wake_downconvert_thread (struct ocfs2_super *osb) { -spin_lock (&osb->dc_task_lock); +unsigned long flags;
Add a blank line between declaration and code.
Quoted 8 lines
fs/ocfs2/dlmglue.c +2
-1+spin_lock_irqsave (&osb->dc_task_lock, flags);/* make sure the voting thread gets a swipe at whatever changes * the caller may have made to the voting state */ osb->dc_wake_sequence++; -spin_unlock (&osb->dc_task_lock); +spin_unlock_irqrestore (&osb->dc_task_lock, flags);wake_up (&osb->dc_event); }