aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input/serio/serio_raw.c
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2011-10-10 21:31:39 -0400
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2011-10-10 21:35:23 -0400
commit550eca7cafa1c6e2c077afb2211a364a982d8645 (patch)
tree1dbb10a995aac0fa624ad86c12ae71378e2bb94e /drivers/input/serio/serio_raw.c
parent8c1c10d5706bbb3b41cb4a5578339d67d3eeffc2 (diff)
Input: serio_raw - fix memory leak when closing char device
Apparently we never freed memory allocated when users open our char devices nor removed old users from the list of connected clients. Also unregister misc device immediately upon disconnecting the port instead of waiting until last user drops off (refcounting in misc device code will make sure needed pieces stay around while they are needed) and make sure we are not holing holding serio_raw_mutex when registering/unregistering misc device. This should fix potential deadlock between serio_raw and misc device code uncovered by lockdep and reported by Thomas Tuttle. Reviewed-by: Wanlong Gao <gaowanlong@cn.fujitsu.com> Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers/input/serio/serio_raw.c')
-rw-r--r--drivers/input/serio/serio_raw.c54
1 files changed, 29 insertions, 25 deletions
diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c
index 830e2fe70a66..4d4cd142bbbb 100644
--- a/drivers/input/serio/serio_raw.c
+++ b/drivers/input/serio/serio_raw.c
@@ -51,7 +51,6 @@ struct serio_raw_client {
51 51
52static DEFINE_MUTEX(serio_raw_mutex); 52static DEFINE_MUTEX(serio_raw_mutex);
53static LIST_HEAD(serio_raw_list); 53static LIST_HEAD(serio_raw_list);
54static unsigned int serio_raw_no;
55 54
56/********************************************************************* 55/*********************************************************************
57 * Interface with userspace (file operations) * 56 * Interface with userspace (file operations) *
@@ -117,14 +116,11 @@ out:
117 return retval; 116 return retval;
118} 117}
119 118
120static void serio_raw_cleanup(struct kref *kref) 119static void serio_raw_free(struct kref *kref)
121{ 120{
122 struct serio_raw *serio_raw = 121 struct serio_raw *serio_raw =
123 container_of(kref, struct serio_raw, kref); 122 container_of(kref, struct serio_raw, kref);
124 123
125 misc_deregister(&serio_raw->dev);
126 list_del_init(&serio_raw->node);
127
128 put_device(&serio_raw->serio->dev); 124 put_device(&serio_raw->serio->dev);
129 kfree(serio_raw); 125 kfree(serio_raw);
130} 126}
@@ -134,11 +130,14 @@ static int serio_raw_release(struct inode *inode, struct file *file)
134 struct serio_raw_client *client = file->private_data; 130 struct serio_raw_client *client = file->private_data;
135 struct serio_raw *serio_raw = client->serio_raw; 131 struct serio_raw *serio_raw = client->serio_raw;
136 132
137 mutex_lock(&serio_raw_mutex); 133 serio_pause_rx(serio_raw->serio);
134 list_del(&client->node);
135 serio_continue_rx(serio_raw->serio);
138 136
139 kref_put(&serio_raw->kref, serio_raw_cleanup); 137 kfree(client);
138
139 kref_put(&serio_raw->kref, serio_raw_free);
140 140
141 mutex_unlock(&serio_raw_mutex);
142 return 0; 141 return 0;
143} 142}
144 143
@@ -281,6 +280,7 @@ static irqreturn_t serio_raw_interrupt(struct serio *serio, unsigned char data,
281 280
282static int serio_raw_connect(struct serio *serio, struct serio_driver *drv) 281static int serio_raw_connect(struct serio *serio, struct serio_driver *drv)
283{ 282{
283 static atomic_t serio_raw_no = ATOMIC_INIT(0);
284 struct serio_raw *serio_raw; 284 struct serio_raw *serio_raw;
285 int err; 285 int err;
286 286
@@ -290,10 +290,8 @@ static int serio_raw_connect(struct serio *serio, struct serio_driver *drv)
290 return -ENOMEM; 290 return -ENOMEM;
291 } 291 }
292 292
293 mutex_lock(&serio_raw_mutex);
294
295 snprintf(serio_raw->name, sizeof(serio_raw->name), 293 snprintf(serio_raw->name, sizeof(serio_raw->name),
296 "serio_raw%d", serio_raw_no++); 294 "serio_raw%ld", (long)atomic_inc_return(&serio_raw_no) - 1);
297 kref_init(&serio_raw->kref); 295 kref_init(&serio_raw->kref);
298 INIT_LIST_HEAD(&serio_raw->client_list); 296 INIT_LIST_HEAD(&serio_raw->client_list);
299 init_waitqueue_head(&serio_raw->wait); 297 init_waitqueue_head(&serio_raw->wait);
@@ -305,9 +303,14 @@ static int serio_raw_connect(struct serio *serio, struct serio_driver *drv)
305 303
306 err = serio_open(serio, drv); 304 err = serio_open(serio, drv);
307 if (err) 305 if (err)
308 goto out_free; 306 goto err_free;
307
308 err = mutex_lock_killable(&serio_raw_mutex);
309 if (err)
310 goto err_close;
309 311
310 list_add_tail(&serio_raw->node, &serio_raw_list); 312 list_add_tail(&serio_raw->node, &serio_raw_list);
313 mutex_unlock(&serio_raw_mutex);
311 314
312 serio_raw->dev.minor = PSMOUSE_MINOR; 315 serio_raw->dev.minor = PSMOUSE_MINOR;
313 serio_raw->dev.name = serio_raw->name; 316 serio_raw->dev.name = serio_raw->name;
@@ -324,22 +327,20 @@ static int serio_raw_connect(struct serio *serio, struct serio_driver *drv)
324 dev_err(&serio->dev, 327 dev_err(&serio->dev,
325 "failed to register raw access device for %s\n", 328 "failed to register raw access device for %s\n",
326 serio->phys); 329 serio->phys);
327 goto out_close; 330 goto err_unlink;
328 } 331 }
329 332
330 dev_info(&serio->dev, "raw access enabled on %s (%s, minor %d)\n", 333 dev_info(&serio->dev, "raw access enabled on %s (%s, minor %d)\n",
331 serio->phys, serio_raw->name, serio_raw->dev.minor); 334 serio->phys, serio_raw->name, serio_raw->dev.minor);
332 goto out; 335 return 0;
333 336
334out_close: 337err_unlink:
335 serio_close(serio);
336 list_del_init(&serio_raw->node); 338 list_del_init(&serio_raw->node);
337out_free: 339err_close:
340 serio_close(serio);
341err_free:
338 serio_set_drvdata(serio, NULL); 342 serio_set_drvdata(serio, NULL);
339 put_device(&serio->dev); 343 kref_put(&serio_raw->kref, serio_raw_free);
340 kfree(serio_raw);
341out:
342 mutex_unlock(&serio_raw_mutex);
343 return err; 344 return err;
344} 345}
345 346
@@ -382,14 +383,17 @@ static void serio_raw_disconnect(struct serio *serio)
382{ 383{
383 struct serio_raw *serio_raw = serio_get_drvdata(serio); 384 struct serio_raw *serio_raw = serio_get_drvdata(serio);
384 385
385 mutex_lock(&serio_raw_mutex); 386 misc_deregister(&serio_raw->dev);
386 387
387 serio_close(serio); 388 mutex_lock(&serio_raw_mutex);
388 serio_raw->dead = true; 389 serio_raw->dead = true;
390 list_del_init(&serio_raw->node);
391 mutex_unlock(&serio_raw_mutex);
392
389 serio_raw_hangup(serio_raw); 393 serio_raw_hangup(serio_raw);
390 kref_put(&serio_raw->kref, serio_raw_cleanup);
391 394
392 mutex_unlock(&serio_raw_mutex); 395 serio_close(serio);
396 kref_put(&serio_raw->kref, serio_raw_free);
393 397
394 serio_set_drvdata(serio, NULL); 398 serio_set_drvdata(serio, NULL);
395} 399}