aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core
diff options
context:
space:
mode:
authorPetr Mladek <pmladek@suse.cz>2014-09-19 11:32:21 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-09-24 01:33:19 -0400
commit32a6958998c52e2b00c2f6459acf9a1f09f054ad (patch)
tree54c95791c1248cdafbec15844dac3b2a56815c18 /drivers/usb/core
parenteb6e29248714d767d5338b8b82d286293e8a041a (diff)
usb: hub: convert khubd into workqueue
There is no need to have separate kthread for handling USB hub events. It is more elegant to use the workqueue framework. The workqueue is allocated as freezable because the original thread was freezable as well. Also it is allocated as ordered because the code is not ready for parallel processing of hub events, see choose_devnum(). struct usb_hub is passed via the work item. Therefore we do not need hub_event_list. Also hub_thread() is not longer needed. It would call only hub_event(). The rest of the code did manipulate the kthread and it is handled by the workqueue framework now. kick_khubd is renamed to kick_hub_wq() to make the function clear. And the protection against races is done another way, see below. hub_event_lock has been removed. It cannot longer be used to protect struct usb_hub between hub_event() and hub_disconnect(). Instead we need to get hub->kref already in kick_hub_wq(). The lock is not really needed for the other scenarios as well. queue_work() returns whether it succeeded. We could revert the needed operations accordingly. This is enough to avoid duplicity and inconsistencies. Yes, the removed lock causes that there is not longer such a strong synchronization between scheduling the work and manipulating hub->disconnected. But kick_hub_wq() must never be called together with hub_disconnect() otherwise even the original code would have failed. Any callers are responsible for this. Therefore the only problem is that hub_disconnect() could be called in parallel with hub_event(). But this was possible even in the past. struct usb_hub is still guarded by hub->kref and released in hub_events() when needed. Note that the source file is still full of the obsolete "khubd" strings. Let's remove them in a follow up patch. This patch already is complex enough. Thanks a lot Alan Stern <stern@rowland.harvard.edu> for code review, many useful tips and guidance. Also thanks to Tejun Heo <tj@kernel.org> for hints how to allocate the workqueue. Signed-off-by: Petr Mladek <pmladek@suse.cz> Acked-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/core')
-rw-r--r--drivers/usb/core/hub.c143
-rw-r--r--drivers/usb/core/hub.h2
2 files changed, 61 insertions, 84 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index b75749c3029a..ed7db5104bcd 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -22,9 +22,8 @@
22#include <linux/usb/hcd.h> 22#include <linux/usb/hcd.h>
23#include <linux/usb/otg.h> 23#include <linux/usb/otg.h>
24#include <linux/usb/quirks.h> 24#include <linux/usb/quirks.h>
25#include <linux/kthread.h> 25#include <linux/workqueue.h>
26#include <linux/mutex.h> 26#include <linux/mutex.h>
27#include <linux/freezer.h>
28#include <linux/random.h> 27#include <linux/random.h>
29#include <linux/pm_qos.h> 28#include <linux/pm_qos.h>
30 29
@@ -42,14 +41,9 @@
42 * change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */ 41 * change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */
43static DEFINE_SPINLOCK(device_state_lock); 42static DEFINE_SPINLOCK(device_state_lock);
44 43
45/* khubd's worklist and its lock */ 44/* workqueue to process hub events */
46static DEFINE_SPINLOCK(hub_event_lock); 45static struct workqueue_struct *hub_wq;
47static LIST_HEAD(hub_event_list); /* List of hubs needing servicing */ 46static void hub_event(struct work_struct *work);
48
49/* Wakes up khubd */
50static DECLARE_WAIT_QUEUE_HEAD(khubd_wait);
51
52static struct task_struct *khubd_task;
53 47
54/* synchronize hub-port add/remove and peering operations */ 48/* synchronize hub-port add/remove and peering operations */
55DEFINE_MUTEX(usb_port_peer_mutex); 49DEFINE_MUTEX(usb_port_peer_mutex);
@@ -105,6 +99,7 @@ EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
105#define HUB_DEBOUNCE_STEP 25 99#define HUB_DEBOUNCE_STEP 25
106#define HUB_DEBOUNCE_STABLE 100 100#define HUB_DEBOUNCE_STABLE 100
107 101
102static void hub_release(struct kref *kref);
108static int usb_reset_and_verify_device(struct usb_device *udev); 103static int usb_reset_and_verify_device(struct usb_device *udev);
109 104
110static inline char *portspeed(struct usb_hub *hub, int portstatus) 105static inline char *portspeed(struct usb_hub *hub, int portstatus)
@@ -576,20 +571,31 @@ static int hub_port_status(struct usb_hub *hub, int port1,
576 return ret; 571 return ret;
577} 572}
578 573
579static void kick_khubd(struct usb_hub *hub) 574static void kick_hub_wq(struct usb_hub *hub)
580{ 575{
581 unsigned long flags; 576 struct usb_interface *intf;
582 577
583 spin_lock_irqsave(&hub_event_lock, flags); 578 if (hub->disconnected || work_pending(&hub->events))
584 if (!hub->disconnected && list_empty(&hub->event_list)) { 579 return;
585 list_add_tail(&hub->event_list, &hub_event_list);
586 580
587 /* Suppress autosuspend until khubd runs */ 581 /*
588 usb_autopm_get_interface_no_resume( 582 * Suppress autosuspend until the event is proceed.
589 to_usb_interface(hub->intfdev)); 583 *
590 wake_up(&khubd_wait); 584 * Be careful and make sure that the symmetric operation is
591 } 585 * always called. We are here only when there is no pending
592 spin_unlock_irqrestore(&hub_event_lock, flags); 586 * work for this hub. Therefore put the interface either when
587 * the new work is called or when it is canceled.
588 */
589 intf = to_usb_interface(hub->intfdev);
590 usb_autopm_get_interface_no_resume(intf);
591 kref_get(&hub->kref);
592
593 if (queue_work(hub_wq, &hub->events))
594 return;
595
596 /* the work has already been scheduled */
597 usb_autopm_put_interface_async(intf);
598 kref_put(&hub->kref, hub_release);
593} 599}
594 600
595void usb_kick_khubd(struct usb_device *hdev) 601void usb_kick_khubd(struct usb_device *hdev)
@@ -597,7 +603,7 @@ void usb_kick_khubd(struct usb_device *hdev)
597 struct usb_hub *hub = usb_hub_to_struct_hub(hdev); 603 struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
598 604
599 if (hub) 605 if (hub)
600 kick_khubd(hub); 606 kick_hub_wq(hub);
601} 607}
602 608
603/* 609/*
@@ -619,7 +625,7 @@ void usb_wakeup_notification(struct usb_device *hdev,
619 hub = usb_hub_to_struct_hub(hdev); 625 hub = usb_hub_to_struct_hub(hdev);
620 if (hub) { 626 if (hub) {
621 set_bit(portnum, hub->wakeup_bits); 627 set_bit(portnum, hub->wakeup_bits);
622 kick_khubd(hub); 628 kick_hub_wq(hub);
623 } 629 }
624} 630}
625EXPORT_SYMBOL_GPL(usb_wakeup_notification); 631EXPORT_SYMBOL_GPL(usb_wakeup_notification);
@@ -659,7 +665,7 @@ static void hub_irq(struct urb *urb)
659 hub->nerrors = 0; 665 hub->nerrors = 0;
660 666
661 /* Something happened, let khubd figure it out */ 667 /* Something happened, let khubd figure it out */
662 kick_khubd(hub); 668 kick_hub_wq(hub);
663 669
664resubmit: 670resubmit:
665 if (hub->quiescing) 671 if (hub->quiescing)
@@ -973,7 +979,7 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
973 */ 979 */
974 980
975 set_bit(port1, hub->change_bits); 981 set_bit(port1, hub->change_bits);
976 kick_khubd(hub); 982 kick_hub_wq(hub);
977} 983}
978 984
979/** 985/**
@@ -1241,7 +1247,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
1241 &hub->leds, LED_CYCLE_PERIOD); 1247 &hub->leds, LED_CYCLE_PERIOD);
1242 1248
1243 /* Scan all ports that need attention */ 1249 /* Scan all ports that need attention */
1244 kick_khubd(hub); 1250 kick_hub_wq(hub);
1245 1251
1246 /* Allow autosuspend if it was suppressed */ 1252 /* Allow autosuspend if it was suppressed */
1247 if (type <= HUB_INIT3) 1253 if (type <= HUB_INIT3)
@@ -1648,14 +1654,11 @@ static void hub_disconnect(struct usb_interface *intf)
1648 struct usb_device *hdev = interface_to_usbdev(intf); 1654 struct usb_device *hdev = interface_to_usbdev(intf);
1649 int port1; 1655 int port1;
1650 1656
1651 /* Take the hub off the event list and don't let it be added again */ 1657 /*
1652 spin_lock_irq(&hub_event_lock); 1658 * Stop adding new hub events. We do not want to block here and thus
1653 if (!list_empty(&hub->event_list)) { 1659 * will not try to remove any pending work item.
1654 list_del_init(&hub->event_list); 1660 */
1655 usb_autopm_put_interface_no_suspend(intf);
1656 }
1657 hub->disconnected = 1; 1661 hub->disconnected = 1;
1658 spin_unlock_irq(&hub_event_lock);
1659 1662
1660 /* Disconnect all children and quiesce the hub */ 1663 /* Disconnect all children and quiesce the hub */
1661 hub->error = 0; 1664 hub->error = 0;
@@ -1795,11 +1798,11 @@ descriptor_error:
1795 } 1798 }
1796 1799
1797 kref_init(&hub->kref); 1800 kref_init(&hub->kref);
1798 INIT_LIST_HEAD(&hub->event_list);
1799 hub->intfdev = &intf->dev; 1801 hub->intfdev = &intf->dev;
1800 hub->hdev = hdev; 1802 hub->hdev = hdev;
1801 INIT_DELAYED_WORK(&hub->leds, led_work); 1803 INIT_DELAYED_WORK(&hub->leds, led_work);
1802 INIT_DELAYED_WORK(&hub->init_work, NULL); 1804 INIT_DELAYED_WORK(&hub->init_work, NULL);
1805 INIT_WORK(&hub->events, hub_event);
1803 usb_get_intf(intf); 1806 usb_get_intf(intf);
1804 usb_get_dev(hdev); 1807 usb_get_dev(hdev);
1805 1808
@@ -4996,9 +4999,8 @@ static void port_event(struct usb_hub *hub, int port1)
4996 hub_port_connect_change(hub, port1, portstatus, portchange); 4999 hub_port_connect_change(hub, port1, portstatus, portchange);
4997} 5000}
4998 5001
4999static void hub_event(void) 5002static void hub_event(struct work_struct *work)
5000{ 5003{
5001 struct list_head *tmp;
5002 struct usb_device *hdev; 5004 struct usb_device *hdev;
5003 struct usb_interface *intf; 5005 struct usb_interface *intf;
5004 struct usb_hub *hub; 5006 struct usb_hub *hub;
@@ -5007,23 +5009,11 @@ static void hub_event(void)
5007 u16 hubchange; 5009 u16 hubchange;
5008 int i, ret; 5010 int i, ret;
5009 5011
5010 /* Grab the first entry at the beginning of the list */ 5012 hub = container_of(work, struct usb_hub, events);
5011 spin_lock_irq(&hub_event_lock);
5012 if (list_empty(&hub_event_list)) {
5013 spin_unlock_irq(&hub_event_lock);
5014 return;
5015 }
5016
5017 tmp = hub_event_list.next;
5018 list_del_init(tmp);
5019
5020 hub = list_entry(tmp, struct usb_hub, event_list);
5021 kref_get(&hub->kref);
5022 spin_unlock_irq(&hub_event_lock);
5023
5024 hdev = hub->hdev; 5013 hdev = hub->hdev;
5025 hub_dev = hub->intfdev; 5014 hub_dev = hub->intfdev;
5026 intf = to_usb_interface(hub_dev); 5015 intf = to_usb_interface(hub_dev);
5016
5027 dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n", 5017 dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",
5028 hdev->state, hdev->maxchild, 5018 hdev->state, hdev->maxchild,
5029 /* NOTE: expects max 15 ports... */ 5019 /* NOTE: expects max 15 ports... */
@@ -5034,20 +5024,20 @@ static void hub_event(void)
5034 * disconnected while waiting for the lock to succeed. */ 5024 * disconnected while waiting for the lock to succeed. */
5035 usb_lock_device(hdev); 5025 usb_lock_device(hdev);
5036 if (unlikely(hub->disconnected)) 5026 if (unlikely(hub->disconnected))
5037 goto out_disconnected; 5027 goto out_hdev_lock;
5038 5028
5039 /* If the hub has died, clean up after it */ 5029 /* If the hub has died, clean up after it */
5040 if (hdev->state == USB_STATE_NOTATTACHED) { 5030 if (hdev->state == USB_STATE_NOTATTACHED) {
5041 hub->error = -ENODEV; 5031 hub->error = -ENODEV;
5042 hub_quiesce(hub, HUB_DISCONNECT); 5032 hub_quiesce(hub, HUB_DISCONNECT);
5043 goto out; 5033 goto out_hdev_lock;
5044 } 5034 }
5045 5035
5046 /* Autoresume */ 5036 /* Autoresume */
5047 ret = usb_autopm_get_interface(intf); 5037 ret = usb_autopm_get_interface(intf);
5048 if (ret) { 5038 if (ret) {
5049 dev_dbg(hub_dev, "Can't autoresume: %d\n", ret); 5039 dev_dbg(hub_dev, "Can't autoresume: %d\n", ret);
5050 goto out; 5040 goto out_hdev_lock;
5051 } 5041 }
5052 5042
5053 /* If this is an inactive hub, do nothing */ 5043 /* If this is an inactive hub, do nothing */
@@ -5124,34 +5114,12 @@ static void hub_event(void)
5124out_autopm: 5114out_autopm:
5125 /* Balance the usb_autopm_get_interface() above */ 5115 /* Balance the usb_autopm_get_interface() above */
5126 usb_autopm_put_interface_no_suspend(intf); 5116 usb_autopm_put_interface_no_suspend(intf);
5127out: 5117out_hdev_lock:
5128 /* Balance the usb_autopm_get_interface_no_resume() in
5129 * kick_khubd() and allow autosuspend.
5130 */
5131 usb_autopm_put_interface(intf);
5132out_disconnected:
5133 usb_unlock_device(hdev); 5118 usb_unlock_device(hdev);
5134 kref_put(&hub->kref, hub_release);
5135}
5136 5119
5137static int hub_thread(void *__unused) 5120 /* Balance the stuff in kick_hub_wq() and allow autosuspend */
5138{ 5121 usb_autopm_put_interface(intf);
5139 /* khubd needs to be freezable to avoid interfering with USB-PERSIST 5122 kref_put(&hub->kref, hub_release);
5140 * port handover. Otherwise it might see that a full-speed device
5141 * was gone before the EHCI controller had handed its port over to
5142 * the companion full-speed controller.
5143 */
5144 set_freezable();
5145
5146 do {
5147 hub_event();
5148 wait_event_freezable(khubd_wait,
5149 !list_empty(&hub_event_list) ||
5150 kthread_should_stop());
5151 } while (!kthread_should_stop() || !list_empty(&hub_event_list));
5152
5153 pr_debug("%s: khubd exiting\n", usbcore_name);
5154 return 0;
5155} 5123}
5156 5124
5157static const struct usb_device_id hub_id_table[] = { 5125static const struct usb_device_id hub_id_table[] = {
@@ -5191,20 +5159,29 @@ int usb_hub_init(void)
5191 return -1; 5159 return -1;
5192 } 5160 }
5193 5161
5194 khubd_task = kthread_run(hub_thread, NULL, "khubd"); 5162 /*
5195 if (!IS_ERR(khubd_task)) 5163 * The workqueue needs to be freezable to avoid interfering with
5164 * USB-PERSIST port handover. Otherwise it might see that a full-speed
5165 * device was gone before the EHCI controller had handed its port
5166 * over to the companion full-speed controller.
5167 *
5168 * Also we use ordered workqueue because the code is not ready
5169 * for parallel execution of hub events, see choose_devnum().
5170 */
5171 hub_wq = alloc_ordered_workqueue("usb_hub_wq", WQ_FREEZABLE);
5172 if (hub_wq)
5196 return 0; 5173 return 0;
5197 5174
5198 /* Fall through if kernel_thread failed */ 5175 /* Fall through if kernel_thread failed */
5199 usb_deregister(&hub_driver); 5176 usb_deregister(&hub_driver);
5200 printk(KERN_ERR "%s: can't start khubd\n", usbcore_name); 5177 pr_err("%s: can't allocate workqueue for usb hub\n", usbcore_name);
5201 5178
5202 return -1; 5179 return -1;
5203} 5180}
5204 5181
5205void usb_hub_cleanup(void) 5182void usb_hub_cleanup(void)
5206{ 5183{
5207 kthread_stop(khubd_task); 5184 destroy_workqueue(hub_wq);
5208 5185
5209 /* 5186 /*
5210 * Hub resources are freed for us by usb_deregister. It calls 5187 * Hub resources are freed for us by usb_deregister. It calls
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index c77d8778af4b..688817fb3246 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -41,7 +41,6 @@ struct usb_hub {
41 int error; /* last reported error */ 41 int error; /* last reported error */
42 int nerrors; /* track consecutive errors */ 42 int nerrors; /* track consecutive errors */
43 43
44 struct list_head event_list; /* hubs w/data or errs ready */
45 unsigned long event_bits[1]; /* status change bitmask */ 44 unsigned long event_bits[1]; /* status change bitmask */
46 unsigned long change_bits[1]; /* ports with logical connect 45 unsigned long change_bits[1]; /* ports with logical connect
47 status change */ 46 status change */
@@ -77,6 +76,7 @@ struct usb_hub {
77 u8 indicator[USB_MAXCHILDREN]; 76 u8 indicator[USB_MAXCHILDREN];
78 struct delayed_work leds; 77 struct delayed_work leds;
79 struct delayed_work init_work; 78 struct delayed_work init_work;
79 struct work_struct events;
80 struct usb_port **ports; 80 struct usb_port **ports;
81}; 81};
82 82