Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b8a7cc4b authored by Marcus Folkesson's avatar Marcus Folkesson Committed by Dmitry Torokhov
Browse files

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: default avatarMarcus Folkesson <marcus.folkesson@gmail.com>
Signed-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent ba521f1b
Loading
Loading
Loading
Loading
+14 −8
Original line number Diff line number Diff line
@@ -82,6 +82,9 @@ struct synusb {
	struct urb *urb;
	unsigned char *data;

	/* serialize access to open/suspend */
	struct mutex pm_mutex;

	/* input device related data structures */
	struct input_dev *input;
	char name[128];
@@ -252,6 +255,7 @@ static int synusb_open(struct input_dev *dev)
		return retval;
	}

	mutex_lock(&synusb->pm_mutex);
	retval = usb_submit_urb(synusb->urb, GFP_KERNEL);
	if (retval) {
		dev_err(&synusb->intf->dev,
@@ -264,6 +268,7 @@ static int synusb_open(struct input_dev *dev)
	synusb->intf->needs_remote_wakeup = 1;

out:
	mutex_unlock(&synusb->pm_mutex);
	usb_autopm_put_interface(synusb->intf);
	return retval;
}
@@ -275,8 +280,10 @@ static void synusb_close(struct input_dev *dev)

	autopm_error = usb_autopm_get_interface(synusb->intf);

	mutex_lock(&synusb->pm_mutex);
	usb_kill_urb(synusb->urb);
	synusb->intf->needs_remote_wakeup = 0;
	mutex_unlock(&synusb->pm_mutex);

	if (!autopm_error)
		usb_autopm_put_interface(synusb->intf);
@@ -315,6 +322,7 @@ static int synusb_probe(struct usb_interface *intf,
	synusb->udev = udev;
	synusb->intf = intf;
	synusb->input = input_dev;
	mutex_init(&synusb->pm_mutex);

	synusb->flags = id->driver_info;
	if (synusb->flags & SYNUSB_COMBO) {
@@ -466,11 +474,10 @@ static void synusb_disconnect(struct usb_interface *intf)
static int synusb_suspend(struct usb_interface *intf, pm_message_t message)
{
	struct synusb *synusb = usb_get_intfdata(intf);
	struct input_dev *input_dev = synusb->input;

	mutex_lock(&input_dev->mutex);
	mutex_lock(&synusb->pm_mutex);
	usb_kill_urb(synusb->urb);
	mutex_unlock(&input_dev->mutex);
	mutex_unlock(&synusb->pm_mutex);

	return 0;
}
@@ -481,14 +488,14 @@ static int synusb_resume(struct usb_interface *intf)
	struct input_dev *input_dev = synusb->input;
	int retval = 0;

	mutex_lock(&input_dev->mutex);
	mutex_lock(&synusb->pm_mutex);

	if ((input_dev->users || (synusb->flags & SYNUSB_IO_ALWAYS)) &&
	    usb_submit_urb(synusb->urb, GFP_NOIO) < 0) {
		retval = -EIO;
	}

	mutex_unlock(&input_dev->mutex);
	mutex_unlock(&synusb->pm_mutex);

	return retval;
}
@@ -496,9 +503,8 @@ static int synusb_resume(struct usb_interface *intf)
static int synusb_pre_reset(struct usb_interface *intf)
{
	struct synusb *synusb = usb_get_intfdata(intf);
	struct input_dev *input_dev = synusb->input;

	mutex_lock(&input_dev->mutex);
	mutex_lock(&synusb->pm_mutex);
	usb_kill_urb(synusb->urb);

	return 0;
@@ -515,7 +521,7 @@ static int synusb_post_reset(struct usb_interface *intf)
		retval = -EIO;
	}

	mutex_unlock(&input_dev->mutex);
	mutex_unlock(&synusb->pm_mutex);

	return retval;
}