diff options
| author | Duoming Zhou <duoming@zju.edu.cn> | 2022-09-18 11:33:12 +0800 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2022-11-25 17:35:41 +0100 |
| commit | 9c7693166b485c224b11fd4c97add7758941a031 (patch) | |
| tree | 5f10944dfffb8034b6a512172d26af525d5e9d08 | |
| parent | f7b525b751a183b4aae0a7b8b7975460d3ba7053 (diff) | |
| download | linux-9c7693166b485c224b11fd4c97add7758941a031.tar.gz linux-9c7693166b485c224b11fd4c97add7758941a031.tar.bz2 linux-9c7693166b485c224b11fd4c97add7758941a031.zip | |
usb: chipidea: fix deadlock in ci_otg_del_timer
commit 7a58b8d6021426b796eebfae80983374d9a80a75 upstream.
There is a deadlock in ci_otg_del_timer(), the process is
shown below:
(thread 1) | (thread 2)
ci_otg_del_timer() | ci_otg_hrtimer_func()
... |
spin_lock_irqsave() //(1) | ...
... |
hrtimer_cancel() | spin_lock_irqsave() //(2)
(block forever)
We hold ci->lock in position (1) and use hrtimer_cancel() to
wait ci_otg_hrtimer_func() to stop, but ci_otg_hrtimer_func()
also need ci->lock in position (2). As a result, the
hrtimer_cancel() in ci_otg_del_timer() will be blocked forever.
This patch extracts hrtimer_cancel() from the protection of
spin_lock_irqsave() in order that the ci_otg_hrtimer_func()
could obtain the ci->lock.
What`s more, there will be no race happen. Because the
"next_timer" is always under the protection of
spin_lock_irqsave() and we only check whether "next_timer"
equals to NUM_OTG_FSM_TIMERS in the following code.
Fixes: 3a316ec4c91c ("usb: chipidea: use hrtimer for otg fsm timers")
Cc: stable <stable@kernel.org>
Signed-off-by: Duoming Zhou <duoming@zju.edu.cn>
Link: https://lore.kernel.org/r/20220918033312.94348-1-duoming@zju.edu.cn
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
| -rw-r--r-- | drivers/usb/chipidea/otg_fsm.c | 2 |
1 files changed, 2 insertions, 0 deletions
diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c index de8e22ec3902..2ec7bd6c50e4 100644 --- a/drivers/usb/chipidea/otg_fsm.c +++ b/drivers/usb/chipidea/otg_fsm.c @@ -260,8 +260,10 @@ static void ci_otg_del_timer(struct ci_hdrc *ci, enum otg_fsm_timer t) ci->enabled_otg_timer_bits &= ~(1 << t); if (ci->next_otg_timer == t) { if (ci->enabled_otg_timer_bits == 0) { + spin_unlock_irqrestore(&ci->lock, flags); /* No enabled timers after delete it */ hrtimer_cancel(&ci->otg_fsm_hrtimer); + spin_lock_irqsave(&ci->lock, flags); ci->next_otg_timer = NUM_OTG_FSM_TIMERS; } else { /* Find the next timer */ |
