Skip to content

Commit 5189df7

Browse files
AlanSterngregkh
authored andcommitted
USB: gadget: dummy-hcd: Fix "task hung" problem
The syzbot fuzzer has been encountering "task hung" problems ever since the dummy-hcd driver was changed to use hrtimers instead of regular timers. It turns out that the problems are caused by a subtle difference between the timer_pending() and hrtimer_active() APIs. The changeover blindly replaced the first by the second. However, timer_pending() returns True when the timer is queued but not when its callback is running, whereas hrtimer_active() returns True when the hrtimer is queued _or_ its callback is running. This difference occasionally caused dummy_urb_enqueue() to think that the callback routine had not yet started when in fact it was almost finished. As a result the hrtimer was not restarted, which made it impossible for the driver to dequeue later the URB that was just enqueued. This caused usb_kill_urb() to hang, and things got worse from there. Since hrtimers have no API for telling when they are queued and the callback isn't running, the driver must keep track of this for itself. That's what this patch does, adding a new "timer_pending" flag and setting or clearing it at the appropriate times. Reported-by: [email protected] Closes: https://lore.kernel.org/linux-usb/[email protected]/ Tested-by: [email protected] Signed-off-by: Alan Stern <[email protected]> Fixes: a7f3813 ("usb: gadget: dummy_hcd: Switch to hrtimer transfer scheduler") Cc: Marcello Sylvester Bauer <[email protected]> Cc: [email protected] Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 9499327 commit 5189df7

File tree

1 file changed

+15
-5
lines changed

1 file changed

+15
-5
lines changed

drivers/usb/gadget/udc/dummy_hcd.c

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ struct dummy_hcd {
254254
u32 stream_en_ep;
255255
u8 num_stream[30 / 2];
256256

257+
unsigned timer_pending:1;
257258
unsigned active:1;
258259
unsigned old_active:1;
259260
unsigned resuming:1;
@@ -1303,9 +1304,11 @@ static int dummy_urb_enqueue(
13031304
urb->error_count = 1; /* mark as a new urb */
13041305

13051306
/* kick the scheduler, it'll do the rest */
1306-
if (!hrtimer_active(&dum_hcd->timer))
1307+
if (!dum_hcd->timer_pending) {
1308+
dum_hcd->timer_pending = 1;
13071309
hrtimer_start(&dum_hcd->timer, ns_to_ktime(DUMMY_TIMER_INT_NSECS),
13081310
HRTIMER_MODE_REL_SOFT);
1311+
}
13091312

13101313
done:
13111314
spin_unlock_irqrestore(&dum_hcd->dum->lock, flags);
@@ -1324,9 +1327,10 @@ static int dummy_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
13241327
spin_lock_irqsave(&dum_hcd->dum->lock, flags);
13251328

13261329
rc = usb_hcd_check_unlink_urb(hcd, urb, status);
1327-
if (!rc && dum_hcd->rh_state != DUMMY_RH_RUNNING &&
1328-
!list_empty(&dum_hcd->urbp_list))
1330+
if (rc == 0 && !dum_hcd->timer_pending) {
1331+
dum_hcd->timer_pending = 1;
13291332
hrtimer_start(&dum_hcd->timer, ns_to_ktime(0), HRTIMER_MODE_REL_SOFT);
1333+
}
13301334

13311335
spin_unlock_irqrestore(&dum_hcd->dum->lock, flags);
13321336
return rc;
@@ -1813,6 +1817,7 @@ static enum hrtimer_restart dummy_timer(struct hrtimer *t)
18131817

18141818
/* look at each urb queued by the host side driver */
18151819
spin_lock_irqsave(&dum->lock, flags);
1820+
dum_hcd->timer_pending = 0;
18161821

18171822
if (!dum_hcd->udev) {
18181823
dev_err(dummy_dev(dum_hcd),
@@ -1994,8 +1999,10 @@ static enum hrtimer_restart dummy_timer(struct hrtimer *t)
19941999
if (list_empty(&dum_hcd->urbp_list)) {
19952000
usb_put_dev(dum_hcd->udev);
19962001
dum_hcd->udev = NULL;
1997-
} else if (dum_hcd->rh_state == DUMMY_RH_RUNNING) {
2002+
} else if (!dum_hcd->timer_pending &&
2003+
dum_hcd->rh_state == DUMMY_RH_RUNNING) {
19982004
/* want a 1 msec delay here */
2005+
dum_hcd->timer_pending = 1;
19992006
hrtimer_start(&dum_hcd->timer, ns_to_ktime(DUMMY_TIMER_INT_NSECS),
20002007
HRTIMER_MODE_REL_SOFT);
20012008
}
@@ -2390,8 +2397,10 @@ static int dummy_bus_resume(struct usb_hcd *hcd)
23902397
} else {
23912398
dum_hcd->rh_state = DUMMY_RH_RUNNING;
23922399
set_link_state(dum_hcd);
2393-
if (!list_empty(&dum_hcd->urbp_list))
2400+
if (!list_empty(&dum_hcd->urbp_list)) {
2401+
dum_hcd->timer_pending = 1;
23942402
hrtimer_start(&dum_hcd->timer, ns_to_ktime(0), HRTIMER_MODE_REL_SOFT);
2403+
}
23952404
hcd->state = HC_STATE_RUNNING;
23962405
}
23972406
spin_unlock_irq(&dum_hcd->dum->lock);
@@ -2522,6 +2531,7 @@ static void dummy_stop(struct usb_hcd *hcd)
25222531
struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd);
25232532

25242533
hrtimer_cancel(&dum_hcd->timer);
2534+
dum_hcd->timer_pending = 0;
25252535
device_remove_file(dummy_dev(dum_hcd), &dev_attr_urbs);
25262536
dev_info(dummy_dev(dum_hcd), "stopped\n");
25272537
}

0 commit comments

Comments
 (0)