From f4042c068ae53625e4a9f39543c2693d07a4e14c Mon Sep 17 00:00:00 2001 From: Thomas Pugliese Date: Mon, 2 Dec 2013 15:39:43 -0600 Subject: usb: wusbcore: send keepalives to unauthenticated devices This patch modifies the WUSB device disconnect timer code to send keepalives to all connected devices even if they are not authenticated. This fixes a problem where unauthenticated devices that lose their connection before they are authenticated will stay in the device tree forever. More importantly, devices in this situation will never relinquish their port on the root hub so eventually all root ports will be taken up and no other devices can connect. A comment in the existing code states that there are some devices that may not respond to keepalives if they have not been authenticated. That comment is about 5 years old and I don't know of any WUSB devices that act that way. Either way, any buggy devices that may still be around will continue to work as long as they can transition to the authenticated state within the WUSB LOA timeout of 4s, which is not unreasonable to expect. Signed-off-by: Thomas Pugliese Signed-off-by: Greg Kroah-Hartman --- drivers/usb/wusbcore/devconnect.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/usb/wusbcore/devconnect.c') diff --git a/drivers/usb/wusbcore/devconnect.c b/drivers/usb/wusbcore/devconnect.c index e538b72c4e3a..d1af4e89111e 100644 --- a/drivers/usb/wusbcore/devconnect.c +++ b/drivers/usb/wusbcore/devconnect.c @@ -411,9 +411,6 @@ static void __wusbhc_dev_disconnect(struct wusbhc *wusbhc, /* * Refresh the list of keep alives to emit in the MMC * - * Some devices don't respond to keep alives unless they've been - * authenticated, so skip unauthenticated devices. - * * We only publish the first four devices that have a coming timeout * condition. Then when we are done processing those, we go for the * next ones. We ignore the ones that have timed out already (they'll @@ -448,7 +445,7 @@ static void __wusbhc_keep_alive(struct wusbhc *wusbhc) if (wusb_dev == NULL) continue; - if (wusb_dev->usb_dev == NULL || !wusb_dev->usb_dev->authenticated) + if (wusb_dev->usb_dev == NULL) continue; if (time_after(jiffies, wusb_dev->entry_ts + tt)) { -- cgit v1.2.2 From 6161ae5f1f371e8ff52306d9a1893f5dec6f60a4 Mon Sep 17 00:00:00 2001 From: Thomas Pugliese Date: Mon, 2 Dec 2013 15:39:44 -0600 Subject: usb: wusbcore: do device lookup while holding the hc mutex This patch modifies the device notification handler to not look up the wusb_dev object before it calls the lower-level handler routines since the wusbhc mutex is not held when calling those routines and the device could go away in the meantime. Instead, let the individual notification handlers get the device ptr if they need to after they have taken the mutex. Signed-off-by: Thomas Pugliese Signed-off-by: Greg Kroah-Hartman --- drivers/usb/wusbcore/devconnect.c | 43 +++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 18 deletions(-) (limited to 'drivers/usb/wusbcore/devconnect.c') diff --git a/drivers/usb/wusbcore/devconnect.c b/drivers/usb/wusbcore/devconnect.c index d1af4e89111e..5107ca999d4c 100644 --- a/drivers/usb/wusbcore/devconnect.c +++ b/drivers/usb/wusbcore/devconnect.c @@ -521,11 +521,19 @@ static struct wusb_dev *wusbhc_find_dev_by_addr(struct wusbhc *wusbhc, u8 addr) * * @wusbhc shall be referenced and unlocked */ -static void wusbhc_handle_dn_alive(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev) +static void wusbhc_handle_dn_alive(struct wusbhc *wusbhc, u8 srcaddr) { + struct wusb_dev *wusb_dev; + mutex_lock(&wusbhc->mutex); - wusb_dev->entry_ts = jiffies; - __wusbhc_keep_alive(wusbhc); + wusb_dev = wusbhc_find_dev_by_addr(wusbhc, srcaddr); + if (wusb_dev == NULL) { + dev_dbg(wusbhc->dev, "ignoring DN_Alive from unconnected device %02x\n", + srcaddr); + } else { + wusb_dev->entry_ts = jiffies; + __wusbhc_keep_alive(wusbhc); + } mutex_unlock(&wusbhc->mutex); } @@ -579,14 +587,22 @@ static void wusbhc_handle_dn_connect(struct wusbhc *wusbhc, * * @wusbhc shall be referenced and unlocked */ -static void wusbhc_handle_dn_disconnect(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev) +static void wusbhc_handle_dn_disconnect(struct wusbhc *wusbhc, u8 srcaddr) { struct device *dev = wusbhc->dev; - - dev_info(dev, "DN DISCONNECT: device 0x%02x going down\n", wusb_dev->addr); + struct wusb_dev *wusb_dev; mutex_lock(&wusbhc->mutex); - __wusbhc_dev_disconnect(wusbhc, wusb_port_by_idx(wusbhc, wusb_dev->port_idx)); + wusb_dev = wusbhc_find_dev_by_addr(wusbhc, srcaddr); + if (wusb_dev == NULL) { + dev_dbg(dev, "ignoring DN DISCONNECT from unconnected device %02x\n", + srcaddr); + } else { + dev_info(dev, "DN DISCONNECT: device 0x%02x going down\n", + wusb_dev->addr); + __wusbhc_dev_disconnect(wusbhc, wusb_port_by_idx(wusbhc, + wusb_dev->port_idx)); + } mutex_unlock(&wusbhc->mutex); } @@ -608,30 +624,21 @@ void wusbhc_handle_dn(struct wusbhc *wusbhc, u8 srcaddr, struct wusb_dn_hdr *dn_hdr, size_t size) { struct device *dev = wusbhc->dev; - struct wusb_dev *wusb_dev; if (size < sizeof(struct wusb_dn_hdr)) { dev_err(dev, "DN data shorter than DN header (%d < %d)\n", (int)size, (int)sizeof(struct wusb_dn_hdr)); return; } - - wusb_dev = wusbhc_find_dev_by_addr(wusbhc, srcaddr); - if (wusb_dev == NULL && dn_hdr->bType != WUSB_DN_CONNECT) { - dev_dbg(dev, "ignoring DN %d from unconnected device %02x\n", - dn_hdr->bType, srcaddr); - return; - } - switch (dn_hdr->bType) { case WUSB_DN_CONNECT: wusbhc_handle_dn_connect(wusbhc, dn_hdr, size); break; case WUSB_DN_ALIVE: - wusbhc_handle_dn_alive(wusbhc, wusb_dev); + wusbhc_handle_dn_alive(wusbhc, srcaddr); break; case WUSB_DN_DISCONNECT: - wusbhc_handle_dn_disconnect(wusbhc, wusb_dev); + wusbhc_handle_dn_disconnect(wusbhc, srcaddr); break; case WUSB_DN_MASAVAILCHANGED: case WUSB_DN_RWAKE: -- cgit v1.2.2 From 471e42ad148c05d091219096726d751684ebf918 Mon Sep 17 00:00:00 2001 From: Thomas Pugliese Date: Mon, 2 Dec 2013 15:39:45 -0600 Subject: usb: wusbcore: fix deadlock in wusbhc_gtk_rekey When multiple wireless USB devices are connected and one of the devices disconnects, the host will distribute a new group key to the remaining devicese using wusbhc_gtk_rekey. wusbhc_gtk_rekey takes the wusbhc->mutex and holds it while it submits a URB to set the new key. This causes a deadlock in wa_urb_enqueue when it calls a device lookup helper function that takes the same lock. This patch changes wusbhc_gtk_rekey to submit a work item to set the GTK so that the URB is submitted without holding wusbhc->mutex. Signed-off-by: Thomas Pugliese Signed-off-by: Greg Kroah-Hartman --- drivers/usb/wusbcore/devconnect.c | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) (limited to 'drivers/usb/wusbcore/devconnect.c') diff --git a/drivers/usb/wusbcore/devconnect.c b/drivers/usb/wusbcore/devconnect.c index 5107ca999d4c..f14e7929ba22 100644 --- a/drivers/usb/wusbcore/devconnect.c +++ b/drivers/usb/wusbcore/devconnect.c @@ -97,18 +97,12 @@ static void wusbhc_devconnect_acked_work(struct work_struct *work); static void wusb_dev_free(struct wusb_dev *wusb_dev) { - if (wusb_dev) { - kfree(wusb_dev->set_gtk_req); - usb_free_urb(wusb_dev->set_gtk_urb); - kfree(wusb_dev); - } + kfree(wusb_dev); } static struct wusb_dev *wusb_dev_alloc(struct wusbhc *wusbhc) { struct wusb_dev *wusb_dev; - struct urb *urb; - struct usb_ctrlrequest *req; wusb_dev = kzalloc(sizeof(*wusb_dev), GFP_KERNEL); if (wusb_dev == NULL) @@ -118,22 +112,6 @@ static struct wusb_dev *wusb_dev_alloc(struct wusbhc *wusbhc) INIT_WORK(&wusb_dev->devconnect_acked_work, wusbhc_devconnect_acked_work); - urb = usb_alloc_urb(0, GFP_KERNEL); - if (urb == NULL) - goto err; - wusb_dev->set_gtk_urb = urb; - - req = kmalloc(sizeof(*req), GFP_KERNEL); - if (req == NULL) - goto err; - wusb_dev->set_gtk_req = req; - - req->bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE; - req->bRequest = USB_REQ_SET_DESCRIPTOR; - req->wValue = cpu_to_le16(USB_DT_KEY << 8 | wusbhc->gtk_index); - req->wIndex = 0; - req->wLength = cpu_to_le16(wusbhc->gtk.descr.bLength); - return wusb_dev; err: wusb_dev_free(wusb_dev); -- cgit v1.2.2