Skip to content

Commit 8520f38

Browse files
AlanSterngregkh
authored andcommitted
USB: change hub initialization sleeps to delayed_work
This patch (as1137) changes the hub_activate() routine, replacing the power-power-up and debounce delays with delayed_work calls. The idea is that on systems where the USB stack is compiled into the kernel rather than built as modules, these delays will no longer block the boot thread. At least 100 ms is saved for each root hub, which can add up to a significant savings in total boot time. Arjan van de Ven was very pleased to see that this shaved 700 ms off his computer's boot time. Since his total boot time is on the order of two seconds, the improvement is considerable. Signed-off-by: Alan Stern <[email protected]> Tested-by: Arjan van de Ven <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 3c4bb71 commit 8520f38

File tree

1 file changed

+81
-10
lines changed

1 file changed

+81
-10
lines changed

drivers/usb/core/hub.c

Lines changed: 81 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ struct usb_hub {
7777
unsigned has_indicators:1;
7878
u8 indicator[USB_MAXCHILDREN];
7979
struct delayed_work leds;
80+
struct delayed_work init_work;
8081
};
8182

8283

@@ -515,10 +516,14 @@ void usb_hub_tt_clear_buffer (struct usb_device *udev, int pipe)
515516
}
516517
EXPORT_SYMBOL_GPL(usb_hub_tt_clear_buffer);
517518

518-
static void hub_power_on(struct usb_hub *hub)
519+
/* If do_delay is false, return the number of milliseconds the caller
520+
* needs to delay.
521+
*/
522+
static unsigned hub_power_on(struct usb_hub *hub, bool do_delay)
519523
{
520524
int port1;
521525
unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2;
526+
unsigned delay;
522527
u16 wHubCharacteristics =
523528
le16_to_cpu(hub->descriptor->wHubCharacteristics);
524529

@@ -537,7 +542,10 @@ static void hub_power_on(struct usb_hub *hub)
537542
set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER);
538543

539544
/* Wait at least 100 msec for power to become stable */
540-
msleep(max(pgood_delay, (unsigned) 100));
545+
delay = max(pgood_delay, (unsigned) 100);
546+
if (do_delay)
547+
msleep(delay);
548+
return delay;
541549
}
542550

543551
static int hub_hub_status(struct usb_hub *hub,
@@ -599,21 +607,55 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
599607
}
600608

601609
enum hub_activation_type {
602-
HUB_INIT, HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME
610+
HUB_INIT, HUB_INIT2, HUB_INIT3,
611+
HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME,
603612
};
604613

614+
static void hub_init_func2(struct work_struct *ws);
615+
static void hub_init_func3(struct work_struct *ws);
616+
605617
static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
606618
{
607619
struct usb_device *hdev = hub->hdev;
608620
int port1;
609621
int status;
610622
bool need_debounce_delay = false;
623+
unsigned delay;
624+
625+
/* Continue a partial initialization */
626+
if (type == HUB_INIT2)
627+
goto init2;
628+
if (type == HUB_INIT3)
629+
goto init3;
611630

612631
/* After a resume, port power should still be on.
613632
* For any other type of activation, turn it on.
614633
*/
615-
if (type != HUB_RESUME)
616-
hub_power_on(hub);
634+
if (type != HUB_RESUME) {
635+
636+
/* Speed up system boot by using a delayed_work for the
637+
* hub's initial power-up delays. This is pretty awkward
638+
* and the implementation looks like a home-brewed sort of
639+
* setjmp/longjmp, but it saves at least 100 ms for each
640+
* root hub (assuming usbcore is compiled into the kernel
641+
* rather than as a module). It adds up.
642+
*
643+
* This can't be done for HUB_RESUME or HUB_RESET_RESUME
644+
* because for those activation types the ports have to be
645+
* operational when we return. In theory this could be done
646+
* for HUB_POST_RESET, but it's easier not to.
647+
*/
648+
if (type == HUB_INIT) {
649+
delay = hub_power_on(hub, false);
650+
PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func2);
651+
schedule_delayed_work(&hub->init_work,
652+
msecs_to_jiffies(delay));
653+
return; /* Continues at init2: below */
654+
} else {
655+
hub_power_on(hub, true);
656+
}
657+
}
658+
init2:
617659

618660
/* Check each port and set hub->change_bits to let khubd know
619661
* which ports need attention.
@@ -692,9 +734,20 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
692734
* If any port-status changes do occur during this delay, khubd
693735
* will see them later and handle them normally.
694736
*/
695-
if (need_debounce_delay)
696-
msleep(HUB_DEBOUNCE_STABLE);
697-
737+
if (need_debounce_delay) {
738+
delay = HUB_DEBOUNCE_STABLE;
739+
740+
/* Don't do a long sleep inside a workqueue routine */
741+
if (type == HUB_INIT2) {
742+
PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func3);
743+
schedule_delayed_work(&hub->init_work,
744+
msecs_to_jiffies(delay));
745+
return; /* Continues at init3: below */
746+
} else {
747+
msleep(delay);
748+
}
749+
}
750+
init3:
698751
hub->quiescing = 0;
699752

700753
status = usb_submit_urb(hub->urb, GFP_NOIO);
@@ -707,6 +760,21 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
707760
kick_khubd(hub);
708761
}
709762

763+
/* Implement the continuations for the delays above */
764+
static void hub_init_func2(struct work_struct *ws)
765+
{
766+
struct usb_hub *hub = container_of(ws, struct usb_hub, init_work.work);
767+
768+
hub_activate(hub, HUB_INIT2);
769+
}
770+
771+
static void hub_init_func3(struct work_struct *ws)
772+
{
773+
struct usb_hub *hub = container_of(ws, struct usb_hub, init_work.work);
774+
775+
hub_activate(hub, HUB_INIT3);
776+
}
777+
710778
enum hub_quiescing_type {
711779
HUB_DISCONNECT, HUB_PRE_RESET, HUB_SUSPEND
712780
};
@@ -716,6 +784,8 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type)
716784
struct usb_device *hdev = hub->hdev;
717785
int i;
718786

787+
cancel_delayed_work_sync(&hub->init_work);
788+
719789
/* khubd and related activity won't re-trigger */
720790
hub->quiescing = 1;
721791

@@ -1099,6 +1169,7 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
10991169
hub->intfdev = &intf->dev;
11001170
hub->hdev = hdev;
11011171
INIT_DELAYED_WORK(&hub->leds, led_work);
1172+
INIT_DELAYED_WORK(&hub->init_work, NULL);
11021173
usb_get_intf(intf);
11031174

11041175
usb_set_intfdata (intf, hub);
@@ -3035,7 +3106,7 @@ static void hub_events(void)
30353106
i);
30363107
clear_port_feature(hdev, i,
30373108
USB_PORT_FEAT_C_OVER_CURRENT);
3038-
hub_power_on(hub);
3109+
hub_power_on(hub, true);
30393110
}
30403111

30413112
if (portchange & USB_PORT_STAT_C_RESET) {
@@ -3070,7 +3141,7 @@ static void hub_events(void)
30703141
dev_dbg (hub_dev, "overcurrent change\n");
30713142
msleep(500); /* Cool down */
30723143
clear_hub_feature(hdev, C_HUB_OVER_CURRENT);
3073-
hub_power_on(hub);
3144+
hub_power_on(hub, true);
30743145
}
30753146
}
30763147

0 commit comments

Comments
 (0)