aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorAristeu Sergio Rozanski Filho <aris@ruivo.org>2009-05-15 01:01:57 -0400
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2009-05-15 22:23:44 -0400
commit05cebd3816dabfb223abe27b3ad3b50140c457a0 (patch)
tree883dbf8b1a8cb1ff4974a902f63ffc75ff890f5d /drivers
parent3d5cb60ef3042ac479dab82e5a945966a0d54d53 (diff)
Input: uinput - flush all pending ff effects before destroying device
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. This patch fixes the problem by flushing all pending FF uploads before destroying the device and preventing new uploads during this operation. [dtor@mail.ru: fix logic that ensures we don't submit new requests to the device that is being destroyed.] Signed-off-by: Aristeu Rozanski <aris@redhat.com> Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/input/misc/uinput.c94
1 files changed, 71 insertions, 23 deletions
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
index 46b7caeb2817..c5a49aba418f 100644
--- a/drivers/input/misc/uinput.c
+++ b/drivers/input/misc/uinput.c
@@ -54,27 +54,28 @@ static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned i
54 return 0; 54 return 0;
55} 55}
56 56
57/* Atomically allocate an ID for the given request. Returns 0 on success. */
57static int uinput_request_alloc_id(struct uinput_device *udev, struct uinput_request *request) 58static int uinput_request_alloc_id(struct uinput_device *udev, struct uinput_request *request)
58{ 59{
59 /* Atomically allocate an ID for the given request. Returns 0 on success. */
60 int id; 60 int id;
61 int err = -1; 61 int err = -1;
62 62
63 spin_lock(&udev->requests_lock); 63 spin_lock(&udev->requests_lock);
64 64
65 for (id = 0; id < UINPUT_NUM_REQUESTS; id++) 65 for (id = 0; id < UINPUT_NUM_REQUESTS; id++) {
66 if (!udev->requests[id]) { 66 if (!udev->requests[id]) {
67 request->id = id; 67 request->id = id;
68 udev->requests[id] = request; 68 udev->requests[id] = request;
69 err = 0; 69 err = 0;
70 break; 70 break;
71 } 71 }
72 }
72 73
73 spin_unlock(&udev->requests_lock); 74 spin_unlock(&udev->requests_lock);
74 return err; 75 return err;
75} 76}
76 77
77static struct uinput_request* uinput_request_find(struct uinput_device *udev, int id) 78static struct uinput_request *uinput_request_find(struct uinput_device *udev, int id)
78{ 79{
79 /* Find an input request, by ID. Returns NULL if the ID isn't valid. */ 80 /* Find an input request, by ID. Returns NULL if the ID isn't valid. */
80 if (id >= UINPUT_NUM_REQUESTS || id < 0) 81 if (id >= UINPUT_NUM_REQUESTS || id < 0)
@@ -99,14 +100,51 @@ static void uinput_request_done(struct uinput_device *udev, struct uinput_reques
99 complete(&request->done); 100 complete(&request->done);
100} 101}
101 102
102static int uinput_request_submit(struct input_dev *dev, struct uinput_request *request) 103static int uinput_request_submit(struct uinput_device *udev, struct uinput_request *request)
103{ 104{
105 int retval;
106
107 retval = uinput_request_reserve_slot(udev, request);
108 if (retval)
109 return retval;
110
111 retval = mutex_lock_interruptible(&udev->mutex);
112 if (retval)
113 return retval;
114
115 if (udev->state != UIST_CREATED) {
116 retval = -ENODEV;
117 goto out;
118 }
119
104 /* Tell our userspace app about this new request by queueing an input event */ 120 /* Tell our userspace app about this new request by queueing an input event */
105 uinput_dev_event(dev, EV_UINPUT, request->code, request->id); 121 uinput_dev_event(udev->dev, EV_UINPUT, request->code, request->id);
122
123 out:
124 mutex_unlock(&udev->mutex);
125 return retval;
126}
127
128/*
129 * Fail all ouitstanding requests so handlers don't wait for the userspace
130 * to finish processing them.
131 */
132static void uinput_flush_requests(struct uinput_device *udev)
133{
134 struct uinput_request *request;
135 int i;
136
137 spin_lock(&udev->requests_lock);
138
139 for (i = 0; i < UINPUT_NUM_REQUESTS; i++) {
140 request = udev->requests[i];
141 if (request) {
142 request->retval = -ENODEV;
143 uinput_request_done(udev, request);
144 }
145 }
106 146
107 /* Wait for the request to complete */ 147 spin_unlock(&udev->requests_lock);
108 wait_for_completion(&request->done);
109 return request->retval;
110} 148}
111 149
112static void uinput_dev_set_gain(struct input_dev *dev, u16 gain) 150static void uinput_dev_set_gain(struct input_dev *dev, u16 gain)
@@ -126,6 +164,7 @@ static int uinput_dev_playback(struct input_dev *dev, int effect_id, int value)
126 164
127static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old) 165static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old)
128{ 166{
167 struct uinput_device *udev = input_get_drvdata(dev);
129 struct uinput_request request; 168 struct uinput_request request;
130 int retval; 169 int retval;
131 170
@@ -146,15 +185,18 @@ static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *eff
146 request.u.upload.effect = effect; 185 request.u.upload.effect = effect;
147 request.u.upload.old = old; 186 request.u.upload.old = old;
148 187
149 retval = uinput_request_reserve_slot(input_get_drvdata(dev), &request); 188 retval = uinput_request_submit(udev, &request);
150 if (!retval) 189 if (!retval) {
151 retval = uinput_request_submit(dev, &request); 190 wait_for_completion(&request.done);
191 retval = request.retval;
192 }
152 193
153 return retval; 194 return retval;
154} 195}
155 196
156static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id) 197static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id)
157{ 198{
199 struct uinput_device *udev = input_get_drvdata(dev);
158 struct uinput_request request; 200 struct uinput_request request;
159 int retval; 201 int retval;
160 202
@@ -166,9 +208,11 @@ static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id)
166 request.code = UI_FF_ERASE; 208 request.code = UI_FF_ERASE;
167 request.u.effect_id = effect_id; 209 request.u.effect_id = effect_id;
168 210
169 retval = uinput_request_reserve_slot(input_get_drvdata(dev), &request); 211 retval = uinput_request_submit(udev, &request);
170 if (!retval) 212 if (!retval) {
171 retval = uinput_request_submit(dev, &request); 213 wait_for_completion(&request.done);
214 retval = request.retval;
215 }
172 216
173 return retval; 217 return retval;
174} 218}
@@ -176,20 +220,24 @@ static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id)
176static void uinput_destroy_device(struct uinput_device *udev) 220static void uinput_destroy_device(struct uinput_device *udev)
177{ 221{
178 const char *name, *phys; 222 const char *name, *phys;
223 struct input_dev *dev = udev->dev;
224 enum uinput_state old_state = udev->state;
179 225
180 if (udev->dev) { 226 udev->state = UIST_NEW_DEVICE;
181 name = udev->dev->name; 227
182 phys = udev->dev->phys; 228 if (dev) {
183 if (udev->state == UIST_CREATED) 229 name = dev->name;
184 input_unregister_device(udev->dev); 230 phys = dev->phys;
185 else 231 if (old_state == UIST_CREATED) {
186 input_free_device(udev->dev); 232 uinput_flush_requests(udev);
233 input_unregister_device(dev);
234 } else {
235 input_free_device(dev);
236 }
187 kfree(name); 237 kfree(name);
188 kfree(phys); 238 kfree(phys);
189 udev->dev = NULL; 239 udev->dev = NULL;
190 } 240 }
191
192 udev->state = UIST_NEW_DEVICE;
193} 241}
194 242
195static int uinput_create_device(struct uinput_device *udev) 243static int uinput_create_device(struct uinput_device *udev)