aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/firewire/fw-cdev.c55
1 files changed, 46 insertions, 9 deletions
diff --git a/drivers/firewire/fw-cdev.c b/drivers/firewire/fw-cdev.c
index ead5c7b06ec1..81362c14ef44 100644
--- a/drivers/firewire/fw-cdev.c
+++ b/drivers/firewire/fw-cdev.c
@@ -20,6 +20,7 @@
20 20
21#include <linux/module.h> 21#include <linux/module.h>
22#include <linux/kernel.h> 22#include <linux/kernel.h>
23#include <linux/kref.h>
23#include <linux/wait.h> 24#include <linux/wait.h>
24#include <linux/errno.h> 25#include <linux/errno.h>
25#include <linux/device.h> 26#include <linux/device.h>
@@ -94,8 +95,27 @@ struct client {
94 unsigned long vm_start; 95 unsigned long vm_start;
95 96
96 struct list_head link; 97 struct list_head link;
98 struct kref kref;
97}; 99};
98 100
101static inline void client_get(struct client *client)
102{
103 kref_get(&client->kref);
104}
105
106static void client_release(struct kref *kref)
107{
108 struct client *client = container_of(kref, struct client, kref);
109
110 fw_device_put(client->device);
111 kfree(client);
112}
113
114static void client_put(struct client *client)
115{
116 kref_put(&client->kref, client_release);
117}
118
99static inline void __user *u64_to_uptr(__u64 value) 119static inline void __user *u64_to_uptr(__u64 value)
100{ 120{
101 return (void __user *)(unsigned long)value; 121 return (void __user *)(unsigned long)value;
@@ -131,6 +151,7 @@ static int fw_device_op_open(struct inode *inode, struct file *file)
131 idr_init(&client->resource_idr); 151 idr_init(&client->resource_idr);
132 INIT_LIST_HEAD(&client->event_list); 152 INIT_LIST_HEAD(&client->event_list);
133 init_waitqueue_head(&client->wait); 153 init_waitqueue_head(&client->wait);
154 kref_init(&client->kref);
134 155
135 file->private_data = client; 156 file->private_data = client;
136 157
@@ -326,6 +347,8 @@ static int add_client_resource(struct client *client,
326 else 347 else
327 ret = idr_get_new(&client->resource_idr, resource, 348 ret = idr_get_new(&client->resource_idr, resource,
328 &resource->handle); 349 &resource->handle);
350 if (ret >= 0)
351 client_get(client);
329 spin_unlock_irqrestore(&client->lock, flags); 352 spin_unlock_irqrestore(&client->lock, flags);
330 353
331 if (ret == -EAGAIN) 354 if (ret == -EAGAIN)
@@ -358,6 +381,8 @@ static int release_client_resource(struct client *client, u32 handle,
358 else 381 else
359 r->release(client, r); 382 r->release(client, r);
360 383
384 client_put(client);
385
361 return 0; 386 return 0;
362} 387}
363 388
@@ -385,11 +410,21 @@ static void complete_transaction(struct fw_card *card, int rcode,
385 410
386 spin_lock_irqsave(&client->lock, flags); 411 spin_lock_irqsave(&client->lock, flags);
387 /* 412 /*
388 * If called while in shutdown, the idr tree must be left untouched. 413 * 1. If called while in shutdown, the idr tree must be left untouched.
389 * The idr handle will be removed later. 414 * The idr handle will be removed and the client reference will be
415 * dropped later.
416 * 2. If the call chain was release_client_resource ->
417 * release_transaction -> complete_transaction (instead of a normal
418 * conclusion of the transaction), i.e. if this resource was already
419 * unregistered from the idr, the client reference will be dropped
420 * by release_client_resource and we must not drop it here.
390 */ 421 */
391 if (!client->in_shutdown) 422 if (!client->in_shutdown &&
423 idr_find(&client->resource_idr, response->resource.handle)) {
392 idr_remove(&client->resource_idr, response->resource.handle); 424 idr_remove(&client->resource_idr, response->resource.handle);
425 /* Drop the idr's reference */
426 client_put(client);
427 }
393 spin_unlock_irqrestore(&client->lock, flags); 428 spin_unlock_irqrestore(&client->lock, flags);
394 429
395 r->type = FW_CDEV_EVENT_RESPONSE; 430 r->type = FW_CDEV_EVENT_RESPONSE;
@@ -408,6 +443,9 @@ static void complete_transaction(struct fw_card *card, int rcode,
408 else 443 else
409 queue_event(client, &response->event, r, sizeof(*r) + r->length, 444 queue_event(client, &response->event, r, sizeof(*r) + r->length,
410 NULL, 0); 445 NULL, 0);
446
447 /* Drop the transaction callback's reference */
448 client_put(client);
411} 449}
412 450
413static int ioctl_send_request(struct client *client, void *buffer) 451static int ioctl_send_request(struct client *client, void *buffer)
@@ -459,6 +497,9 @@ static int ioctl_send_request(struct client *client, void *buffer)
459 if (ret < 0) 497 if (ret < 0)
460 goto failed; 498 goto failed;
461 499
500 /* Get a reference for the transaction callback */
501 client_get(client);
502
462 fw_send_request(device->card, &response->transaction, 503 fw_send_request(device->card, &response->transaction,
463 request->tcode & 0x1f, 504 request->tcode & 0x1f,
464 device->node->node_id, 505 device->node->node_id,
@@ -1044,6 +1085,7 @@ static int shutdown_resource(int id, void *p, void *data)
1044 struct client *client = data; 1085 struct client *client = data;
1045 1086
1046 r->release(client, r); 1087 r->release(client, r);
1088 client_put(client);
1047 1089
1048 return 0; 1090 return 0;
1049} 1091}
@@ -1076,12 +1118,7 @@ static int fw_device_op_release(struct inode *inode, struct file *file)
1076 list_for_each_entry_safe(e, next_e, &client->event_list, link) 1118 list_for_each_entry_safe(e, next_e, &client->event_list, link)
1077 kfree(e); 1119 kfree(e);
1078 1120
1079 /* 1121 client_put(client);
1080 * FIXME: client should be reference-counted. It's extremely unlikely
1081 * but there may still be transactions being completed at this point.
1082 */
1083 fw_device_put(client->device);
1084 kfree(client);
1085 1122
1086 return 0; 1123 return 0;
1087} 1124}