aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2007-05-04 11:55:11 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2007-07-12 19:29:46 -0400
commite8054854221d9d51e381c64d365404f4c1c30f50 (patch)
treef7efd45bce5842dde7986aa700dab09548e8c7d4 /drivers/usb
parent06b84e8adcad8280d76a7c71e772c5cddba96d85 (diff)
USB: make hub driver's release more robust
This revised patch (as893c) improves the method used by the hub driver to release its private data structure. The current code is non-robust, relying on a memory region not getting reused by another driver after it has been freed. The patch adds a reference count to the structure, resolving the question of when to release it. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/core/hub.c59
1 files changed, 31 insertions, 28 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 9464eb504ae6..77a6627b18d2 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -34,6 +34,7 @@
34struct usb_hub { 34struct usb_hub {
35 struct device *intfdev; /* the "interface" device */ 35 struct device *intfdev; /* the "interface" device */
36 struct usb_device *hdev; 36 struct usb_device *hdev;
37 struct kref kref;
37 struct urb *urb; /* for interrupt polling pipe */ 38 struct urb *urb; /* for interrupt polling pipe */
38 39
39 /* buffer for urb ... with extra space in case of babble */ 40 /* buffer for urb ... with extra space in case of babble */
@@ -66,6 +67,7 @@ struct usb_hub {
66 unsigned limited_power:1; 67 unsigned limited_power:1;
67 unsigned quiescing:1; 68 unsigned quiescing:1;
68 unsigned activating:1; 69 unsigned activating:1;
70 unsigned disconnected:1;
69 71
70 unsigned has_indicators:1; 72 unsigned has_indicators:1;
71 u8 indicator[USB_MAXCHILDREN]; 73 u8 indicator[USB_MAXCHILDREN];
@@ -321,7 +323,7 @@ static void kick_khubd(struct usb_hub *hub)
321 to_usb_interface(hub->intfdev)->pm_usage_cnt = 1; 323 to_usb_interface(hub->intfdev)->pm_usage_cnt = 1;
322 324
323 spin_lock_irqsave(&hub_event_lock, flags); 325 spin_lock_irqsave(&hub_event_lock, flags);
324 if (list_empty(&hub->event_list)) { 326 if (!hub->disconnected & list_empty(&hub->event_list)) {
325 list_add_tail(&hub->event_list, &hub_event_list); 327 list_add_tail(&hub->event_list, &hub_event_list);
326 wake_up(&khubd_wait); 328 wake_up(&khubd_wait);
327 } 329 }
@@ -330,6 +332,7 @@ static void kick_khubd(struct usb_hub *hub)
330 332
331void usb_kick_khubd(struct usb_device *hdev) 333void usb_kick_khubd(struct usb_device *hdev)
332{ 334{
335 /* FIXME: What if hdev isn't bound to the hub driver? */
333 kick_khubd(hdev_to_hub(hdev)); 336 kick_khubd(hdev_to_hub(hdev));
334} 337}
335 338
@@ -845,43 +848,42 @@ fail:
845 return ret; 848 return ret;
846} 849}
847 850
851static void hub_release(struct kref *kref)
852{
853 struct usb_hub *hub = container_of(kref, struct usb_hub, kref);
854
855 usb_put_intf(to_usb_interface(hub->intfdev));
856 kfree(hub);
857}
858
848static unsigned highspeed_hubs; 859static unsigned highspeed_hubs;
849 860
850static void hub_disconnect(struct usb_interface *intf) 861static void hub_disconnect(struct usb_interface *intf)
851{ 862{
852 struct usb_hub *hub = usb_get_intfdata (intf); 863 struct usb_hub *hub = usb_get_intfdata (intf);
853 struct usb_device *hdev; 864
865 /* Take the hub off the event list and don't let it be added again */
866 spin_lock_irq(&hub_event_lock);
867 list_del_init(&hub->event_list);
868 hub->disconnected = 1;
869 spin_unlock_irq(&hub_event_lock);
854 870
855 /* Disconnect all children and quiesce the hub */ 871 /* Disconnect all children and quiesce the hub */
856 hub->error = 0; 872 hub->error = 0;
857 hub_pre_reset(intf); 873 hub_pre_reset(intf);
858 874
859 usb_set_intfdata (intf, NULL); 875 usb_set_intfdata (intf, NULL);
860 hdev = hub->hdev;
861 876
862 if (hdev->speed == USB_SPEED_HIGH) 877 if (hub->hdev->speed == USB_SPEED_HIGH)
863 highspeed_hubs--; 878 highspeed_hubs--;
864 879
865 usb_free_urb(hub->urb); 880 usb_free_urb(hub->urb);
866 hub->urb = NULL;
867
868 spin_lock_irq(&hub_event_lock);
869 list_del_init(&hub->event_list);
870 spin_unlock_irq(&hub_event_lock);
871
872 kfree(hub->descriptor); 881 kfree(hub->descriptor);
873 hub->descriptor = NULL;
874
875 kfree(hub->status); 882 kfree(hub->status);
876 hub->status = NULL; 883 usb_buffer_free(hub->hdev, sizeof(*hub->buffer), hub->buffer,
877 884 hub->buffer_dma);
878 if (hub->buffer) {
879 usb_buffer_free(hdev, sizeof(*hub->buffer), hub->buffer,
880 hub->buffer_dma);
881 hub->buffer = NULL;
882 }
883 885
884 kfree(hub); 886 kref_put(&hub->kref, hub_release);
885} 887}
886 888
887static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id) 889static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
@@ -929,10 +931,12 @@ descriptor_error:
929 return -ENOMEM; 931 return -ENOMEM;
930 } 932 }
931 933
934 kref_init(&hub->kref);
932 INIT_LIST_HEAD(&hub->event_list); 935 INIT_LIST_HEAD(&hub->event_list);
933 hub->intfdev = &intf->dev; 936 hub->intfdev = &intf->dev;
934 hub->hdev = hdev; 937 hub->hdev = hdev;
935 INIT_DELAYED_WORK(&hub->leds, led_work); 938 INIT_DELAYED_WORK(&hub->leds, led_work);
939 usb_get_intf(intf);
936 940
937 usb_set_intfdata (intf, hub); 941 usb_set_intfdata (intf, hub);
938 intf->needs_remote_wakeup = 1; 942 intf->needs_remote_wakeup = 1;
@@ -2534,10 +2538,12 @@ static void hub_events(void)
2534 list_del_init(tmp); 2538 list_del_init(tmp);
2535 2539
2536 hub = list_entry(tmp, struct usb_hub, event_list); 2540 hub = list_entry(tmp, struct usb_hub, event_list);
2537 hdev = hub->hdev; 2541 kref_get(&hub->kref);
2538 intf = to_usb_interface(hub->intfdev); 2542 spin_unlock_irq(&hub_event_lock);
2539 hub_dev = &intf->dev;
2540 2543
2544 hdev = hub->hdev;
2545 hub_dev = hub->intfdev;
2546 intf = to_usb_interface(hub_dev);
2541 dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n", 2547 dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",
2542 hdev->state, hub->descriptor 2548 hdev->state, hub->descriptor
2543 ? hub->descriptor->bNbrPorts 2549 ? hub->descriptor->bNbrPorts
@@ -2546,13 +2552,10 @@ static void hub_events(void)
2546 (u16) hub->change_bits[0], 2552 (u16) hub->change_bits[0],
2547 (u16) hub->event_bits[0]); 2553 (u16) hub->event_bits[0]);
2548 2554
2549 usb_get_intf(intf);
2550 spin_unlock_irq(&hub_event_lock);
2551
2552 /* Lock the device, then check to see if we were 2555 /* Lock the device, then check to see if we were
2553 * disconnected while waiting for the lock to succeed. */ 2556 * disconnected while waiting for the lock to succeed. */
2554 usb_lock_device(hdev); 2557 usb_lock_device(hdev);
2555 if (hub != usb_get_intfdata(intf)) 2558 if (unlikely(hub->disconnected))
2556 goto loop; 2559 goto loop;
2557 2560
2558 /* If the hub has died, clean up after it */ 2561 /* If the hub has died, clean up after it */
@@ -2715,7 +2718,7 @@ loop_autopm:
2715 usb_autopm_enable(intf); 2718 usb_autopm_enable(intf);
2716loop: 2719loop:
2717 usb_unlock_device(hdev); 2720 usb_unlock_device(hdev);
2718 usb_put_intf(intf); 2721 kref_put(&hub->kref, hub_release);
2719 2722
2720 } /* end while (1) */ 2723 } /* end while (1) */
2721} 2724}