usb: dwc3: rockchip: fix possible circular deadlock
authorWilliam Wu <wulf@rock-chips.com>
Mon, 10 Apr 2017 12:33:06 +0000 (20:33 +0800)
committerHuang, Tao <huangtao@rock-chips.com>
Tue, 11 Apr 2017 03:07:40 +0000 (11:07 +0800)
This patch aims to fix possible unsafe locking scenario when
dwc3 probe failed. The possible deadlock warning info is:

[4.254320] ======================================================
[4.254327] [ INFO: possible circular locking dependency detected ]
[4.254334] 4.4.55 #20 Not tainted
[4.254339] -------------------------------------------------------
[4.254346] kworker/4:1/47 is trying to acquire lock:
[4.254352]  (&rockchip->lock){+.+.+.}, at: [<ffffff8008730d58>] dwc3_rockchip_otg_extcon_evt_work+0x30/0x42c
[4.254382]
[4.254382] but task is already holding lock:
[4.254388]  ((&rockchip->otg_work)){+.+...}, at: [<ffffff80080b7a78>] process_one_work+0x1c4/0x6ac
[4.254411]
[4.254411] which lock already depends on the new lock.
[4.254411]
[4.254418]
[4.254418] the existing dependency chain (in reverse order) is:
[4.254424]
-> #1 ((&rockchip->otg_work)){+.+...}:
[4.254440]        [<ffffff80080f6a58>] __lock_acquire+0x15b4/0x1950
[4.254452]        [<ffffff80080f75fc>] lock_acquire+0x190/0x250
[4.254461]        [<ffffff80080b88c8>] flush_work+0x4c/0x274
[4.254471]        [<ffffff80080b8cd8>] __cancel_work_timer+0x130/0x1c0
[4.254480]        [<ffffff80080b8d78>] cancel_work_sync+0x10/0x18
[4.254489]        [<ffffff8008731648>] dwc3_rockchip_probe+0x4f4/0x59c
[4.254498]        [<ffffff8008525a9c>] platform_drv_probe+0x58/0xa4
[4.254509]        [<ffffff800852397c>] driver_probe_device+0x118/0x2b0
[4.254521]        [<ffffff8008523b80>] __driver_attach+0x6c/0x98
[4.254531]        [<ffffff80085229d8>] bus_for_each_dev+0x80/0xb0
[4.254543]        [<ffffff80085234b0>] driver_attach+0x20/0x28
[4.254554]        [<ffffff8008523040>] bus_add_driver+0xe8/0x1ec
[4.254565]        [<ffffff8008524a9c>] driver_register+0x94/0xe0
[4.254576]        [<ffffff80085259f4>] __platform_driver_register+0x48/0x50
[4.254585]        [<ffffff8009130bf4>] dwc3_rockchip_driver_init+0x18/0x20
[4.254598]        [<ffffff8008082ba8>] do_one_initcall+0x180/0x19c
[4.254609]        [<ffffff8009100e58>] kernel_init_freeable+0x208/0x2c0
[4.254621]        [<ffffff8008c00748>] kernel_init+0x10/0xf8
[4.254632]        [<ffffff80080826d0>] ret_from_fork+0x10/0x40
[4.254641]
-> #0 (&rockchip->lock){+.+.+.}:
[4.254656]        [<ffffff80080f3af8>] print_circular_bug+0x64/0x2c4
[4.254666]        [<ffffff80080f6728>] __lock_acquire+0x1284/0x1950
[4.254675]        [<ffffff80080f75fc>] lock_acquire+0x190/0x250
[4.254685]        [<ffffff8008c03aac>] mutex_lock_nested+0x80/0x3d0
[4.254695]        [<ffffff8008730d58>] dwc3_rockchip_otg_extcon_evt_work+0x30/0x42c
[4.254704]        [<ffffff80080b7bec>] process_one_work+0x338/0x6ac
[4.254714]        [<ffffff80080b90e8>] worker_thread+0x300/0x428
[4.254723]        [<ffffff80080bead8>] kthread+0xf4/0xfc
[4.254732]        [<ffffff80080826d0>] ret_from_fork+0x10/0x40
[4.254741]
[4.254741] other info that might help us debug this:
[4.254741]
[4.254749]  Possible unsafe locking scenario:
[4.254749]
[4.254755]        CPU0                    CPU1
[4.254760]        ----                    ----
[4.254765]   lock((&rockchip->otg_work));
[4.254775]                                lock(&rockchip->lock);
[4.254786]                                lock((&rockchip->otg_work));
[4.254796]   lock(&rockchip->lock);
[4.254805]
[4.254805]  *** DEADLOCK ***
[4.254805]
[4.254813] 2 locks held by kworker/4:1/47:
[4.254818]  #0:  ("events"){.+.+.+}, at: [<ffffff80080b7a78>] process_one_work+0x1c4/0x6ac
[4.254839]  #1:  ((&rockchip->otg_work)){+.+...}, at: [<ffffff80080b7a78>] process_one_work+0x1c4/0x6ac
[4.254860]
[4.254860] stack backtrace:
[4.254869] CPU: 4 PID: 47 Comm: kworker/4:1 Not tainted 4.4.55 #20
[4.254875] Hardware name: Rockchip RK3399 Evaluation Board v3 (Android) (DT)
[4.254885] Workqueue: events dwc3_rockchip_otg_extcon_evt_work
[4.254893] Call trace:
[4.254902] [<ffffff8008088a1c>] dump_backtrace+0x0/0x1c8
[4.254909] [<ffffff8008088bf8>] show_stack+0x14/0x1c
[4.254917] [<ffffff80083a5fa0>] dump_stack+0xb0/0xec
[4.254925] [<ffffff80080f3d3c>] print_circular_bug+0x2a8/0x2c4
[4.254931] [<ffffff80080f6728>] __lock_acquire+0x1284/0x1950
[4.254938] [<ffffff80080f75fc>] lock_acquire+0x190/0x250
[4.254946] [<ffffff8008c03aac>] mutex_lock_nested+0x80/0x3d0
[4.254952] [<ffffff8008730d58>] dwc3_rockchip_otg_extcon_evt_work+0x30/0x42c
[4.254960] [<ffffff80080b7bec>] process_one_work+0x338/0x6ac
[4.254967] [<ffffff80080b90e8>] worker_thread+0x300/0x428
[4.254973] [<ffffff80080bead8>] kthread+0xf4/0xfc
[4.254979] [<ffffff80080826d0>] ret_from_fork+0x10/0x40
[4.275124] rockchip-dwc3 usb@fe800000: USB peripheral connected

Change-Id: I9d34724e1c2af9b8472f79bfe4b088cbdde6394d
Signed-off-by: William Wu <wulf@rock-chips.com>
drivers/usb/dwc3/dwc3-rockchip.c

index 1c8829b..a6e5cbd 100644 (file)
@@ -676,6 +676,8 @@ static void dwc3_rockchip_extcon_unregister(struct dwc3_rockchip *rockchip)
                                   &rockchip->device_nb);
        extcon_unregister_notifier(rockchip->edev, EXTCON_USB_HOST,
                                   &rockchip->host_nb);
+
+       cancel_work_sync(&rockchip->otg_work);
 }
 
 static int dwc3_rockchip_probe(struct platform_device *pdev)
@@ -684,6 +686,7 @@ static int dwc3_rockchip_probe(struct platform_device *pdev)
        struct device           *dev = &pdev->dev;
        struct device_node      *np = dev->of_node, *child;
        struct platform_device  *child_pdev;
+       struct usb_hcd          *hcd = NULL;
 
        unsigned int            count;
        int                     ret;
@@ -776,24 +779,24 @@ static int dwc3_rockchip_probe(struct platform_device *pdev)
                goto err2;
        }
 
+       if (rockchip->dwc->dr_mode == USB_DR_MODE_HOST ||
+           rockchip->dwc->dr_mode == USB_DR_MODE_OTG) {
+               hcd = dev_get_drvdata(&rockchip->dwc->xhci->dev);
+               if (!hcd) {
+                       dev_err(dev, "fail to get drvdata hcd\n");
+                       ret = -EPROBE_DEFER;
+                       goto err2;
+               }
+       }
+
        ret = dwc3_rockchip_extcon_register(rockchip);
        if (ret < 0)
                goto err2;
 
        if (rockchip->edev || (rockchip->dwc->dr_mode == USB_DR_MODE_OTG)) {
-               if (rockchip->dwc->dr_mode == USB_DR_MODE_HOST ||
-                   rockchip->dwc->dr_mode == USB_DR_MODE_OTG) {
-                       struct usb_hcd *hcd =
-                               dev_get_drvdata(&rockchip->dwc->xhci->dev);
-                       if (!hcd) {
-                               dev_err(dev, "fail to get drvdata hcd\n");
-                               ret = -EPROBE_DEFER;
-                               goto err3;
-                       }
-                       if (hcd->state != HC_STATE_HALT) {
-                               usb_remove_hcd(hcd->shared_hcd);
-                               usb_remove_hcd(hcd);
-                       }
+               if (hcd && hcd->state != HC_STATE_HALT) {
+                       usb_remove_hcd(hcd->shared_hcd);
+                       usb_remove_hcd(hcd);
                }
 
                pm_runtime_set_autosuspend_delay(&child_pdev->dev,
@@ -815,11 +818,7 @@ static int dwc3_rockchip_probe(struct platform_device *pdev)
 
        return ret;
 
-err3:
-       dwc3_rockchip_extcon_unregister(rockchip);
-
 err2:
-       cancel_work_sync(&rockchip->otg_work);
        of_platform_depopulate(dev);
 
 err1: