diff options
author | Marcus Folkesson <marcus.folkesson@gmail.com> | 2018-03-17 13:49:46 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2018-03-17 14:05:18 -0400 |
commit | b8a7cc4b5e5a0681fdeffe92b613e3e4eb769801 (patch) | |
tree | 64fa737a3a633e60a11682413d9ed1d8d33c36dd | |
parent | ba521f1bd202237d2c1dea95e4213169b8c6ba01 (diff) |
Input: synaptics_usb - fix deadlock in autosuspend
usb_autopm_get_interface() that is called in synusb_open() does an
autoresume if the device is suspended.
input_dev->mutex used in synusb_resume() is in this case already
taken by the input subsystem and will cause a deadlock.
Signed-off-by: Marcus Folkesson <marcus.folkesson@gmail.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
-rw-r--r-- | drivers/input/mouse/synaptics_usb.c | 22 |
1 files changed, 14 insertions, 8 deletions
diff --git a/drivers/input/mouse/synaptics_usb.c b/drivers/input/mouse/synaptics_usb.c index cb7d15d826d0..2c66913cf5a2 100644 --- a/drivers/input/mouse/synaptics_usb.c +++ b/drivers/input/mouse/synaptics_usb.c | |||
@@ -82,6 +82,9 @@ struct synusb { | |||
82 | struct urb *urb; | 82 | struct urb *urb; |
83 | unsigned char *data; | 83 | unsigned char *data; |
84 | 84 | ||
85 | /* serialize access to open/suspend */ | ||
86 | struct mutex pm_mutex; | ||
87 | |||
85 | /* input device related data structures */ | 88 | /* input device related data structures */ |
86 | struct input_dev *input; | 89 | struct input_dev *input; |
87 | char name[128]; | 90 | char name[128]; |
@@ -252,6 +255,7 @@ static int synusb_open(struct input_dev *dev) | |||
252 | return retval; | 255 | return retval; |
253 | } | 256 | } |
254 | 257 | ||
258 | mutex_lock(&synusb->pm_mutex); | ||
255 | retval = usb_submit_urb(synusb->urb, GFP_KERNEL); | 259 | retval = usb_submit_urb(synusb->urb, GFP_KERNEL); |
256 | if (retval) { | 260 | if (retval) { |
257 | dev_err(&synusb->intf->dev, | 261 | dev_err(&synusb->intf->dev, |
@@ -264,6 +268,7 @@ static int synusb_open(struct input_dev *dev) | |||
264 | synusb->intf->needs_remote_wakeup = 1; | 268 | synusb->intf->needs_remote_wakeup = 1; |
265 | 269 | ||
266 | out: | 270 | out: |
271 | mutex_unlock(&synusb->pm_mutex); | ||
267 | usb_autopm_put_interface(synusb->intf); | 272 | usb_autopm_put_interface(synusb->intf); |
268 | return retval; | 273 | return retval; |
269 | } | 274 | } |
@@ -275,8 +280,10 @@ static void synusb_close(struct input_dev *dev) | |||
275 | 280 | ||
276 | autopm_error = usb_autopm_get_interface(synusb->intf); | 281 | autopm_error = usb_autopm_get_interface(synusb->intf); |
277 | 282 | ||
283 | mutex_lock(&synusb->pm_mutex); | ||
278 | usb_kill_urb(synusb->urb); | 284 | usb_kill_urb(synusb->urb); |
279 | synusb->intf->needs_remote_wakeup = 0; | 285 | synusb->intf->needs_remote_wakeup = 0; |
286 | mutex_unlock(&synusb->pm_mutex); | ||
280 | 287 | ||
281 | if (!autopm_error) | 288 | if (!autopm_error) |
282 | usb_autopm_put_interface(synusb->intf); | 289 | usb_autopm_put_interface(synusb->intf); |
@@ -315,6 +322,7 @@ static int synusb_probe(struct usb_interface *intf, | |||
315 | synusb->udev = udev; | 322 | synusb->udev = udev; |
316 | synusb->intf = intf; | 323 | synusb->intf = intf; |
317 | synusb->input = input_dev; | 324 | synusb->input = input_dev; |
325 | mutex_init(&synusb->pm_mutex); | ||
318 | 326 | ||
319 | synusb->flags = id->driver_info; | 327 | synusb->flags = id->driver_info; |
320 | if (synusb->flags & SYNUSB_COMBO) { | 328 | if (synusb->flags & SYNUSB_COMBO) { |
@@ -466,11 +474,10 @@ static void synusb_disconnect(struct usb_interface *intf) | |||
466 | static int synusb_suspend(struct usb_interface *intf, pm_message_t message) | 474 | static int synusb_suspend(struct usb_interface *intf, pm_message_t message) |
467 | { | 475 | { |
468 | struct synusb *synusb = usb_get_intfdata(intf); | 476 | struct synusb *synusb = usb_get_intfdata(intf); |
469 | struct input_dev *input_dev = synusb->input; | ||
470 | 477 | ||
471 | mutex_lock(&input_dev->mutex); | 478 | mutex_lock(&synusb->pm_mutex); |
472 | usb_kill_urb(synusb->urb); | 479 | usb_kill_urb(synusb->urb); |
473 | mutex_unlock(&input_dev->mutex); | 480 | mutex_unlock(&synusb->pm_mutex); |
474 | 481 | ||
475 | return 0; | 482 | return 0; |
476 | } | 483 | } |
@@ -481,14 +488,14 @@ static int synusb_resume(struct usb_interface *intf) | |||
481 | struct input_dev *input_dev = synusb->input; | 488 | struct input_dev *input_dev = synusb->input; |
482 | int retval = 0; | 489 | int retval = 0; |
483 | 490 | ||
484 | mutex_lock(&input_dev->mutex); | 491 | mutex_lock(&synusb->pm_mutex); |
485 | 492 | ||
486 | if ((input_dev->users || (synusb->flags & SYNUSB_IO_ALWAYS)) && | 493 | if ((input_dev->users || (synusb->flags & SYNUSB_IO_ALWAYS)) && |
487 | usb_submit_urb(synusb->urb, GFP_NOIO) < 0) { | 494 | usb_submit_urb(synusb->urb, GFP_NOIO) < 0) { |
488 | retval = -EIO; | 495 | retval = -EIO; |
489 | } | 496 | } |
490 | 497 | ||
491 | mutex_unlock(&input_dev->mutex); | 498 | mutex_unlock(&synusb->pm_mutex); |
492 | 499 | ||
493 | return retval; | 500 | return retval; |
494 | } | 501 | } |
@@ -496,9 +503,8 @@ static int synusb_resume(struct usb_interface *intf) | |||
496 | static int synusb_pre_reset(struct usb_interface *intf) | 503 | static int synusb_pre_reset(struct usb_interface *intf) |
497 | { | 504 | { |
498 | struct synusb *synusb = usb_get_intfdata(intf); | 505 | struct synusb *synusb = usb_get_intfdata(intf); |
499 | struct input_dev *input_dev = synusb->input; | ||
500 | 506 | ||
501 | mutex_lock(&input_dev->mutex); | 507 | mutex_lock(&synusb->pm_mutex); |
502 | usb_kill_urb(synusb->urb); | 508 | usb_kill_urb(synusb->urb); |
503 | 509 | ||
504 | return 0; | 510 | return 0; |
@@ -515,7 +521,7 @@ static int synusb_post_reset(struct usb_interface *intf) | |||
515 | retval = -EIO; | 521 | retval = -EIO; |
516 | } | 522 | } |
517 | 523 | ||
518 | mutex_unlock(&input_dev->mutex); | 524 | mutex_unlock(&synusb->pm_mutex); |
519 | 525 | ||
520 | return retval; | 526 | return retval; |
521 | } | 527 | } |