From mboxrd@z Thu Jan 1 00:00:00 1970 From: akpm@linux-foundation.org Subject: [patch 6/9] uinput: flush all pending ff effects before destroying device Date: Tue, 12 May 2009 13:43:09 -0700 Message-ID: <200905122102.n4CL2PVs006896@imap1.linux-foundation.org> Return-path: Received: from smtp1.linux-foundation.org ([140.211.169.13]:60395 "EHLO smtp1.linux-foundation.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752769AbZELVKN (ORCPT ); Tue, 12 May 2009 17:10:13 -0400 Sender: linux-input-owner@vger.kernel.org List-Id: linux-input@vger.kernel.org To: dtor@mail.ru Cc: linux-input@vger.kernel.org, akpm@linux-foundation.org, aris@ruivo.org, aris@redhat.com From: Aristeu Sergio Rozanski Filho 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: [] ? __lock_acquire+0xb3a/0xc01 [] ? mark_lock+0x22/0x3a2 [] ? mark_held_locks+0x67/0x82 [] ? __mutex_lock_common+0x1f0/0x35d [] __mutex_lock_common+0x200/0x35d [] ? evdev_cleanup+0x2f/0x103 [] ? lock_release_holdtime+0x2c/0x111 [] ? evdev_cleanup+0x2f/0x103 [] ? kobject_put+0x47/0x4b [] mutex_lock_nested+0x35/0x3a [] evdev_cleanup+0x2f/0x103 [] evdev_disconnect+0x2b/0x44 [] input_unregister_device+0x11a/0x1c9 [] uinput_destroy_device+0x2d/0x5d [] uinput_release+0x18/0x26 [] __fput+0xf0/0x18b [] fput+0x15/0x17 [] filp_close+0x67/0x72 [] put_files_struct+0x74/0xc8 [] exit_files+0x47/0x4f [] do_exit+0x228/0x89b [] ? _spin_unlock_irq+0x2b/0x37 [] do_group_exit+0x79/0xa9 [] get_signal_to_deliver+0x318/0x339 [] do_notify_resume+0x8c/0x875 [] ? lock_release_holdtime+0x2c/0x111 [] ? _spin_unlock_irq+0x2b/0x37 [] ? trace_hardirqs_on_caller+0xf6/0x11a [] ? trace_hardirqs_on+0xd/0xf [] ? _spin_unlock_irq+0x2b/0x37 [] ? finish_task_switch+0x5f/0xc2 [] ? finish_task_switch+0x0/0xc2 [] ? do_vfs_ioctl+0x398/0x3c6 [] ? retint_signal+0x11/0xc2 [] ? trace_hardirqs_on_caller+0xf6/0x11a [] 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: [] ? mark_lock+0x22/0x3a2 [] ? pvclock_clocksource_read+0x42/0x7e [] ? mark_lock+0x22/0x3a2 [] schedule_timeout+0x22/0xc6 [] ? _spin_unlock_irq+0x2b/0x37 [] ? trace_hardirqs_on_caller+0xf6/0x11a [] ? trace_hardirqs_on+0xd/0xf [] ? _spin_unlock_irq+0x2b/0x37 [] wait_for_common+0xb7/0x100 [] ? default_wake_function+0x0/0xf [] wait_for_completion+0x18/0x1a [] uinput_dev_upload_effect+0x80/0x93 [] input_ff_upload+0x1eb/0x276 [] evdev_ioctl_handler+0x6f1/0x709 [] ? sched_clock+0x9/0xc [] ? lock_release_holdtime+0x2c/0x111 [] evdev_ioctl+0xb/0xd [] vfs_ioctl+0x2a/0x78 [] do_vfs_ioctl+0x398/0x3c6 [] ? fsnotify_modify+0x62/0x6a [] ? sysret_check+0x46/0x81 [] sys_ioctl+0x42/0x65 [] 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 Cc: Dmitry Torokhov Signed-off-by: Andrew Morton --- 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__ */ _