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

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

Input: pagasus_notetaker - fix deadlock in autosuspend



usb_autopm_get_interface() that is called in pegasus_open() does an
autoresume if the device is suspended.

input_dev->mutex used in pegasus_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 ff0d2cba
Loading
Loading
Loading
Loading
+19 −6
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@
#include <linux/usb/input.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/mutex.h>

/* USB HID defines */
#define USB_REQ_GET_REPORT		0x01
@@ -76,6 +77,10 @@ struct pegasus {
	struct usb_device *usbdev;
	struct usb_interface *intf;
	struct urb *irq;

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

	char name[128];
	char phys[64];
	struct work_struct init;
@@ -216,6 +221,7 @@ static int pegasus_open(struct input_dev *dev)
	if (error)
		return error;

	mutex_lock(&pegasus->pm_mutex);
	pegasus->irq->dev = pegasus->usbdev;
	if (usb_submit_urb(pegasus->irq, GFP_KERNEL)) {
		error = -EIO;
@@ -226,12 +232,14 @@ static int pegasus_open(struct input_dev *dev)
	if (error)
		goto err_kill_urb;

	mutex_unlock(&pegasus->pm_mutex);
	return 0;

err_kill_urb:
	usb_kill_urb(pegasus->irq);
	cancel_work_sync(&pegasus->init);
err_autopm_put:
	mutex_unlock(&pegasus->pm_mutex);
	usb_autopm_put_interface(pegasus->intf);
	return error;
}
@@ -240,8 +248,11 @@ static void pegasus_close(struct input_dev *dev)
{
	struct pegasus *pegasus = input_get_drvdata(dev);

	mutex_lock(&pegasus->pm_mutex);
	usb_kill_urb(pegasus->irq);
	cancel_work_sync(&pegasus->init);
	mutex_unlock(&pegasus->pm_mutex);

	usb_autopm_put_interface(pegasus->intf);
}

@@ -274,6 +285,8 @@ static int pegasus_probe(struct usb_interface *intf,
		goto err_free_mem;
	}

	mutex_init(&pegasus->pm_mutex);

	pegasus->usbdev = dev;
	pegasus->dev = input_dev;
	pegasus->intf = intf;
@@ -388,10 +401,10 @@ static int pegasus_suspend(struct usb_interface *intf, pm_message_t message)
{
	struct pegasus *pegasus = usb_get_intfdata(intf);

	mutex_lock(&pegasus->dev->mutex);
	mutex_lock(&pegasus->pm_mutex);
	usb_kill_urb(pegasus->irq);
	cancel_work_sync(&pegasus->init);
	mutex_unlock(&pegasus->dev->mutex);
	mutex_unlock(&pegasus->pm_mutex);

	return 0;
}
@@ -401,10 +414,10 @@ static int pegasus_resume(struct usb_interface *intf)
	struct pegasus *pegasus = usb_get_intfdata(intf);
	int retval = 0;

	mutex_lock(&pegasus->dev->mutex);
	mutex_lock(&pegasus->pm_mutex);
	if (pegasus->dev->users && usb_submit_urb(pegasus->irq, GFP_NOIO) < 0)
		retval = -EIO;
	mutex_unlock(&pegasus->dev->mutex);
	mutex_unlock(&pegasus->pm_mutex);

	return retval;
}
@@ -414,14 +427,14 @@ static int pegasus_reset_resume(struct usb_interface *intf)
	struct pegasus *pegasus = usb_get_intfdata(intf);
	int retval = 0;

	mutex_lock(&pegasus->dev->mutex);
	mutex_lock(&pegasus->pm_mutex);
	if (pegasus->dev->users) {
		retval = pegasus_set_mode(pegasus, PEN_MODE_XY,
					  NOTETAKER_LED_MOUSE);
		if (!retval && usb_submit_urb(pegasus->irq, GFP_NOIO) < 0)
			retval = -EIO;
	}
	mutex_unlock(&pegasus->dev->mutex);
	mutex_unlock(&pegasus->pm_mutex);

	return retval;
}