[patch 6/9] uinput: flush all pending ff effects before destroying device

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Aristeu Sergio Rozanski Filho <aris@xxxxxxxxx>

The destruction of a input device in uinput is triggered by an ioctl(). 
If a process tries to destroy an input device while other is uploading a
force feedback effect by evdev to the same device, they'll deadlock:

	uinput-driver D ffff88001f994540  3120  2403   2374
	 ffff88001dc459e8 0000000000000046 ffff88001dc45988 ffffffff8106a2d8
	 ffffffff81a4a980 ffffffff81a4a980 ffff88001f994540 ffff88001ea3c540
	 ffff88001f9948e0 00000000810297b8 0000000100000001 ffff88001f9948e0
	Call Trace:
	 [<ffffffff8106a2d8>] ? __lock_acquire+0xb3a/0xc01
	 [<ffffffff81068a7b>] ? mark_lock+0x22/0x3a2
	 [<ffffffff81068e62>] ? mark_held_locks+0x67/0x82
	 [<ffffffff813148c6>] ? __mutex_lock_common+0x1f0/0x35d
	 [<ffffffff813148d6>] __mutex_lock_common+0x200/0x35d
	 [<ffffffff81251699>] ? evdev_cleanup+0x2f/0x103
	 [<ffffffff8106763a>] ? lock_release_holdtime+0x2c/0x111
	 [<ffffffff81251699>] ? evdev_cleanup+0x2f/0x103
	 [<ffffffff81158b16>] ? kobject_put+0x47/0x4b
	 [<ffffffff81314adc>] mutex_lock_nested+0x35/0x3a
	 [<ffffffff81251699>] evdev_cleanup+0x2f/0x103
	 [<ffffffff81251798>] evdev_disconnect+0x2b/0x44
	 [<ffffffff8124cc02>] input_unregister_device+0x11a/0x1c9
	 [<ffffffff81258505>] uinput_destroy_device+0x2d/0x5d
	 [<ffffffff8125854d>] uinput_release+0x18/0x26
	 [<ffffffff810cc5b9>] __fput+0xf0/0x18b
	 [<ffffffff810cc669>] fput+0x15/0x17
	 [<ffffffff810c99f8>] filp_close+0x67/0x72
	 [<ffffffff81049255>] put_files_struct+0x74/0xc8
	 [<ffffffff810492f0>] exit_files+0x47/0x4f
	 [<ffffffff8104ab49>] do_exit+0x228/0x89b
	 [<ffffffff81316002>] ? _spin_unlock_irq+0x2b/0x37
	 [<ffffffff8104b235>] do_group_exit+0x79/0xa9
	 [<ffffffff81054a37>] get_signal_to_deliver+0x318/0x339
	 [<ffffffff810103fb>] do_notify_resume+0x8c/0x875
	 [<ffffffff8106763a>] ? lock_release_holdtime+0x2c/0x111
	 [<ffffffff81316002>] ? _spin_unlock_irq+0x2b/0x37
	 [<ffffffff81069007>] ? trace_hardirqs_on_caller+0xf6/0x11a
	 [<ffffffff81069038>] ? trace_hardirqs_on+0xd/0xf
	 [<ffffffff81316002>] ? _spin_unlock_irq+0x2b/0x37
	 [<ffffffff81043f97>] ? finish_task_switch+0x5f/0xc2
	 [<ffffffff81043f38>] ? finish_task_switch+0x0/0xc2
	 [<ffffffff810d71e2>] ? do_vfs_ioctl+0x398/0x3c6
	 [<ffffffff810119af>] ? retint_signal+0x11/0xc2
	 [<ffffffff81069007>] ? trace_hardirqs_on_caller+0xf6/0x11a
	 [<ffffffff81011a03>] retint_signal+0x65/0xc2
	uinput-app    D 0000000000000002  3120  2413   2374
	 ffff88001d941be8 0000000000000046 ffff88001d941b38 ffffffff81068a7b
	 ffffffff81a4a980 ffffffff81a4a980 ffff88001d96a2a0 ffff88001f994540
	 ffff88001d96a640 000000000000004e ffff88001d941bc8 ffff88001d96a640
	Call Trace:
	 [<ffffffff81068a7b>] ? mark_lock+0x22/0x3a2
	 [<ffffffff810297b8>] ? pvclock_clocksource_read+0x42/0x7e
	 [<ffffffff81068a7b>] ? mark_lock+0x22/0x3a2
	 [<ffffffff813142c6>] schedule_timeout+0x22/0xc6
	 [<ffffffff81316002>] ? _spin_unlock_irq+0x2b/0x37
	 [<ffffffff81069007>] ? trace_hardirqs_on_caller+0xf6/0x11a
	 [<ffffffff81069038>] ? trace_hardirqs_on+0xd/0xf
	 [<ffffffff81316002>] ? _spin_unlock_irq+0x2b/0x37
	 [<ffffffff813141a2>] wait_for_common+0xb7/0x100
	 [<ffffffff8103fcde>] ? default_wake_function+0x0/0xf
	 [<ffffffff81314275>] wait_for_completion+0x18/0x1a
	 [<ffffffff81258acf>] uinput_dev_upload_effect+0x80/0x93
	 [<ffffffff8124e6d1>] input_ff_upload+0x1eb/0x276
	 [<ffffffff8125130f>] evdev_ioctl_handler+0x6f1/0x709
	 [<ffffffff81017a68>] ? sched_clock+0x9/0xc
	 [<ffffffff8106763a>] ? lock_release_holdtime+0x2c/0x111
	 [<ffffffff81251344>] evdev_ioctl+0xb/0xd
	 [<ffffffff810d6dfc>] vfs_ioctl+0x2a/0x78
	 [<ffffffff810d71e2>] do_vfs_ioctl+0x398/0x3c6
	 [<ffffffff810cb067>] ? fsnotify_modify+0x62/0x6a
	 [<ffffffff810110ca>] ? sysret_check+0x46/0x81
	 [<ffffffff810d7252>] sys_ioctl+0x42/0x65
	 [<ffffffff8101107a>] system_call_fastpath+0x16/0x1b

This patch fixes the problem by flushing all pending FF uploads before
destroying the device and preventing new uploads during this operation

Signed-off-by: Aristeu Rozanski <aris@xxxxxxxxxx>
Cc: Dmitry Torokhov <dtor@xxxxxxx>
Signed-off-by: Andrew Morton <akpm@xxxxxxxxxxxxxxxxxxxx>
---

 drivers/input/misc/uinput.c |   41 ++++++++++++++++++++++++++++++----
 include/linux/uinput.h      |    1 
 2 files changed, 38 insertions(+), 4 deletions(-)

diff -puN drivers/input/misc/uinput.c~uinput-flush-all-pending-ff-effects-before-destroying-device drivers/input/misc/uinput.c
--- a/drivers/input/misc/uinput.c~uinput-flush-all-pending-ff-effects-before-destroying-device
+++ a/drivers/input/misc/uinput.c
@@ -62,6 +62,9 @@ static int uinput_request_alloc_id(struc
 
 	spin_lock(&udev->requests_lock);
 
+	if (unlikely(udev->requests_flushed))
+		goto out;
+
 	for (id = 0; id < UINPUT_NUM_REQUESTS; id++)
 		if (!udev->requests[id]) {
 			request->id = id;
@@ -70,6 +73,7 @@ static int uinput_request_alloc_id(struc
 			break;
 		}
 
+out:
 	spin_unlock(&udev->requests_lock);
 	return err;
 }
@@ -109,6 +113,34 @@ static int uinput_request_submit(struct 
 	return request->retval;
 }
 
+static inline void uinput_init_requests(struct uinput_device *udev)
+{
+	spin_lock_init(&udev->requests_lock);
+	init_waitqueue_head(&udev->requests_waitq);
+	udev->requests_flushed = 0;
+}
+
+static void uinput_flush_requests(struct uinput_device *udev)
+{
+	struct uinput_request *request;
+	int id;
+
+	/*
+	 * get rid of all pending requests and set requests_flushed to prevent
+	 * more requests to be filled until we unregister the input device
+	 */
+	spin_lock(&udev->requests_lock);
+	udev->requests_flushed = 1;
+	for (id = 0; id < UINPUT_NUM_REQUESTS; id++) {
+		request = udev->requests[id];
+		if (request == NULL)
+			continue;
+		request->retval = -ENODEV;
+		uinput_request_done(udev, request);
+	}
+	spin_unlock(&udev->requests_lock);
+}
+
 static void uinput_dev_set_gain(struct input_dev *dev, u16 gain)
 {
 	uinput_dev_event(dev, EV_FF, FF_GAIN, gain);
@@ -180,9 +212,11 @@ static void uinput_destroy_device(struct
 	if (udev->dev) {
 		name = udev->dev->name;
 		phys = udev->dev->phys;
-		if (udev->state == UIST_CREATED)
+		if (udev->state == UIST_CREATED) {
+			uinput_flush_requests(udev);
 			input_unregister_device(udev->dev);
-		else
+			uinput_init_requests(udev);
+		} else
 			input_free_device(udev->dev);
 		kfree(name);
 		kfree(phys);
@@ -237,8 +271,7 @@ static int uinput_open(struct inode *ino
 
 	lock_kernel();
 	mutex_init(&newdev->mutex);
-	spin_lock_init(&newdev->requests_lock);
-	init_waitqueue_head(&newdev->requests_waitq);
+	uinput_init_requests(newdev);
 	init_waitqueue_head(&newdev->waitq);
 	newdev->state = UIST_NEW_DEVICE;
 
diff -puN include/linux/uinput.h~uinput-flush-all-pending-ff-effects-before-destroying-device include/linux/uinput.h
--- a/include/linux/uinput.h~uinput-flush-all-pending-ff-effects-before-destroying-device
+++ a/include/linux/uinput.h
@@ -74,6 +74,7 @@ struct uinput_device {
 	struct uinput_request	*requests[UINPUT_NUM_REQUESTS];
 	wait_queue_head_t	requests_waitq;
 	spinlock_t		requests_lock;
+	int			requests_flushed;
 };
 #endif	/* __KERNEL__ */
 
_
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux