Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4b607108 authored by Duoming Zhou's avatar Duoming Zhou Committed by Greg Kroah-Hartman
Browse files

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: 3a316ec4 ("usb: chipidea: use hrtimer for otg fsm timers")
Cc: stable <stable@kernel.org>
Signed-off-by: default avatarDuoming Zhou <duoming@zju.edu.cn>
Link: https://lore.kernel.org/r/20220918033312.94348-1-duoming@zju.edu.cn


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent b87527bd
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -256,8 +256,10 @@ static void ci_otg_del_timer(struct ci_hdrc *ci, enum otg_fsm_timer t)
	ci->enabled_otg_timer_bits &= ~(1 << t);
	ci->enabled_otg_timer_bits &= ~(1 << t);
	if (ci->next_otg_timer == t) {
	if (ci->next_otg_timer == t) {
		if (ci->enabled_otg_timer_bits == 0) {
		if (ci->enabled_otg_timer_bits == 0) {
			spin_unlock_irqrestore(&ci->lock, flags);
			/* No enabled timers after delete it */
			/* No enabled timers after delete it */
			hrtimer_cancel(&ci->otg_fsm_hrtimer);
			hrtimer_cancel(&ci->otg_fsm_hrtimer);
			spin_lock_irqsave(&ci->lock, flags);
			ci->next_otg_timer = NUM_OTG_FSM_TIMERS;
			ci->next_otg_timer = NUM_OTG_FSM_TIMERS;
		} else {
		} else {
			/* Find the next timer */
			/* Find the next timer */