usb: wusbcore: fix device disconnect on rekey timeout
authorThomas Pugliese <thomas.pugliese@gmail.com>
Tue, 16 Sep 2014 21:40:15 +0000 (16:40 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 24 Sep 2014 05:06:59 +0000 (22:06 -0700)
If three or more wireless devices are connected and two of them
disconnect between 1-3 seconds apart, it can cause the HWA to disconnect
the remaining devices due to failing to see a DN_Alive message from
them.  This happens because when the HWA detects that the first device
is gone, it will attempt to rekey the remaining devices.  If one of the
devices is not responding because it has also been disconnected but not
yet timed out, the synchronous rekey operation running on the wusbd
workqueue can block for up to 5 seconds.  This will prevent the
KEEPALIVE timer from running and DN_Alive messages from being processed
because they are processed by the same workqueue.  This patch moves the
rekey operation to a separate workqueue since it is the only wusb work
item that needs to communicate directly with wireless devices.  The rest
of the WUSB work items either perform no device IO or communicate
directly with the host controller and should not be blocked out by a
non-responding wireless device.

Signed-off-by: Thomas Pugliese <thomas.pugliese@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/wusbcore/security.c
drivers/usb/wusbcore/wusbhc.h

index 95be9953cd47ce89353313c255d57d72582420d0..cc74d669c802399c2293bd04f1d073fbcd7dee82 100644 (file)
@@ -33,6 +33,20 @@ static void wusbhc_gtk_rekey_work(struct work_struct *work);
 
 int wusbhc_sec_create(struct wusbhc *wusbhc)
 {
+       /*
+        * WQ is singlethread because we need to serialize rekey operations.
+        * Use a separate workqueue for security operations instead of the
+        * wusbd workqueue because security operations may need to communicate
+        * directly with downstream wireless devices using synchronous URBs.
+        * If a device is not responding, this could block other host
+        * controller operations.
+        */
+       wusbhc->wq_security = create_singlethread_workqueue("wusbd_security");
+       if (wusbhc->wq_security == NULL) {
+               pr_err("WUSB-core: Cannot create wusbd_security workqueue\n");
+               return -ENOMEM;
+       }
+
        wusbhc->gtk.descr.bLength = sizeof(wusbhc->gtk.descr) +
                sizeof(wusbhc->gtk.data);
        wusbhc->gtk.descr.bDescriptorType = USB_DT_KEY;
@@ -48,6 +62,7 @@ int wusbhc_sec_create(struct wusbhc *wusbhc)
 /* Called when the HC is destroyed */
 void wusbhc_sec_destroy(struct wusbhc *wusbhc)
 {
+       destroy_workqueue(wusbhc->wq_security);
 }
 
 
@@ -596,5 +611,5 @@ void wusbhc_gtk_rekey(struct wusbhc *wusbhc)
         * and will cause a deadlock.  Instead, queue a work item to do
         * it when the lock is not held
         */
-       queue_work(wusbd, &wusbhc->gtk_rekey_work);
+       queue_work(wusbhc->wq_security, &wusbhc->gtk_rekey_work);
 }
index 2384add453716e1eea74b380694921f2f27e7b66..41838db7f85c6bb508ff7c570d73fb529a50282a 100644 (file)
@@ -295,6 +295,9 @@ struct wusbhc {
        } __attribute__((packed)) gtk;
        u8 gtk_index;
        u32 gtk_tkid;
+
+       /* workqueue for WUSB security related tasks. */
+       struct workqueue_struct *wq_security;
        struct work_struct gtk_rekey_work;
 
        struct usb_encryption_descriptor *ccm1_etd;