aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorJay Fenlason <fenlason@redhat.com>2008-12-21 10:47:17 -0500
committerStefan Richter <stefanr@s5r6.in-berlin.de>2009-03-24 15:56:38 -0400
commit45ee3199eb3e4233b755a9bb353a0527a4c58b5f (patch)
tree4fa2ab6e54af7cb6aceab1cea1b5676836644841 /drivers
parent97811e347310766030a648fdf0e407b2c91a39c1 (diff)
firewire: cdev: use an idr rather than a linked list for resources
The current code uses a linked list and a counter for storing resources and the corresponding handle numbers. By changing to an idr we can be safe from counter wrap-around giving two resources the same handle. Furthermore, the deallocation ioctls now check whether the resource to be freed is of the intended type. Signed-off-by: Jay Fenlason <fenlason@redhat.com> Some rework by Stefan R: - The idr API documentation says we get an ID within 0...0x7fffffff. Hence we can rest assured that idr handles fit into cdev handles. - Fix some races. Add a client->in_shutdown flag for this purpose. - Add allocation retry to add_client_resource(). - It is possible to use idr_for_each() in fw_device_op_release(). - Fix ioctl_send_response() regression. - Small style changes. Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/firewire/fw-cdev.c165
1 files changed, 114 insertions, 51 deletions
diff --git a/drivers/firewire/fw-cdev.c b/drivers/firewire/fw-cdev.c
index 4dd66c1a36da..094aee5c8bcf 100644
--- a/drivers/firewire/fw-cdev.c
+++ b/drivers/firewire/fw-cdev.c
@@ -41,10 +41,12 @@
41#include "fw-device.h" 41#include "fw-device.h"
42 42
43struct client; 43struct client;
44struct client_resource;
45typedef void (*client_resource_release_fn_t)(struct client *,
46 struct client_resource *);
44struct client_resource { 47struct client_resource {
45 struct list_head link; 48 client_resource_release_fn_t release;
46 void (*release)(struct client *client, struct client_resource *r); 49 int handle;
47 u32 handle;
48}; 50};
49 51
50/* 52/*
@@ -78,9 +80,10 @@ struct iso_interrupt {
78struct client { 80struct client {
79 u32 version; 81 u32 version;
80 struct fw_device *device; 82 struct fw_device *device;
83
81 spinlock_t lock; 84 spinlock_t lock;
82 u32 resource_handle; 85 bool in_shutdown;
83 struct list_head resource_list; 86 struct idr resource_idr;
84 struct list_head event_list; 87 struct list_head event_list;
85 wait_queue_head_t wait; 88 wait_queue_head_t wait;
86 u64 bus_reset_closure; 89 u64 bus_reset_closure;
@@ -126,9 +129,9 @@ static int fw_device_op_open(struct inode *inode, struct file *file)
126 } 129 }
127 130
128 client->device = device; 131 client->device = device;
129 INIT_LIST_HEAD(&client->event_list);
130 INIT_LIST_HEAD(&client->resource_list);
131 spin_lock_init(&client->lock); 132 spin_lock_init(&client->lock);
133 idr_init(&client->resource_idr);
134 INIT_LIST_HEAD(&client->event_list);
132 init_waitqueue_head(&client->wait); 135 init_waitqueue_head(&client->wait);
133 136
134 file->private_data = client; 137 file->private_data = client;
@@ -151,7 +154,10 @@ static void queue_event(struct client *client, struct event *event,
151 event->v[1].size = size1; 154 event->v[1].size = size1;
152 155
153 spin_lock_irqsave(&client->lock, flags); 156 spin_lock_irqsave(&client->lock, flags);
154 list_add_tail(&event->link, &client->event_list); 157 if (client->in_shutdown)
158 kfree(event);
159 else
160 list_add_tail(&event->link, &client->event_list);
155 spin_unlock_irqrestore(&client->lock, flags); 161 spin_unlock_irqrestore(&client->lock, flags);
156 162
157 wake_up_interruptible(&client->wait); 163 wake_up_interruptible(&client->wait);
@@ -310,34 +316,49 @@ static int ioctl_get_info(struct client *client, void *buffer)
310 return 0; 316 return 0;
311} 317}
312 318
313static void 319static int
314add_client_resource(struct client *client, struct client_resource *resource) 320add_client_resource(struct client *client, struct client_resource *resource,
321 gfp_t gfp_mask)
315{ 322{
316 unsigned long flags; 323 unsigned long flags;
324 int ret;
325
326 retry:
327 if (idr_pre_get(&client->resource_idr, gfp_mask) == 0)
328 return -ENOMEM;
317 329
318 spin_lock_irqsave(&client->lock, flags); 330 spin_lock_irqsave(&client->lock, flags);
319 list_add_tail(&resource->link, &client->resource_list); 331 if (client->in_shutdown)
320 resource->handle = client->resource_handle++; 332 ret = -ECANCELED;
333 else
334 ret = idr_get_new(&client->resource_idr, resource,
335 &resource->handle);
321 spin_unlock_irqrestore(&client->lock, flags); 336 spin_unlock_irqrestore(&client->lock, flags);
337
338 if (ret == -EAGAIN)
339 goto retry;
340
341 return ret < 0 ? ret : 0;
322} 342}
323 343
324static int 344static int
325release_client_resource(struct client *client, u32 handle, 345release_client_resource(struct client *client, u32 handle,
346 client_resource_release_fn_t release,
326 struct client_resource **resource) 347 struct client_resource **resource)
327{ 348{
328 struct client_resource *r; 349 struct client_resource *r;
329 unsigned long flags; 350 unsigned long flags;
330 351
331 spin_lock_irqsave(&client->lock, flags); 352 spin_lock_irqsave(&client->lock, flags);
332 list_for_each_entry(r, &client->resource_list, link) { 353 if (client->in_shutdown)
333 if (r->handle == handle) { 354 r = NULL;
334 list_del(&r->link); 355 else
335 break; 356 r = idr_find(&client->resource_idr, handle);
336 } 357 if (r && r->release == release)
337 } 358 idr_remove(&client->resource_idr, handle);
338 spin_unlock_irqrestore(&client->lock, flags); 359 spin_unlock_irqrestore(&client->lock, flags);
339 360
340 if (&r->link == &client->resource_list) 361 if (!(r && r->release == release))
341 return -EINVAL; 362 return -EINVAL;
342 363
343 if (resource) 364 if (resource)
@@ -372,7 +393,12 @@ complete_transaction(struct fw_card *card, int rcode,
372 memcpy(r->data, payload, r->length); 393 memcpy(r->data, payload, r->length);
373 394
374 spin_lock_irqsave(&client->lock, flags); 395 spin_lock_irqsave(&client->lock, flags);
375 list_del(&response->resource.link); 396 /*
397 * If called while in shutdown, the idr tree must be left untouched.
398 * The idr handle will be removed later.
399 */
400 if (!client->in_shutdown)
401 idr_remove(&client->resource_idr, response->resource.handle);
376 spin_unlock_irqrestore(&client->lock, flags); 402 spin_unlock_irqrestore(&client->lock, flags);
377 403
378 r->type = FW_CDEV_EVENT_RESPONSE; 404 r->type = FW_CDEV_EVENT_RESPONSE;
@@ -416,7 +442,7 @@ static int ioctl_send_request(struct client *client, void *buffer)
416 copy_from_user(response->response.data, 442 copy_from_user(response->response.data,
417 u64_to_uptr(request->data), request->length)) { 443 u64_to_uptr(request->data), request->length)) {
418 ret = -EFAULT; 444 ret = -EFAULT;
419 goto err; 445 goto failed;
420 } 446 }
421 447
422 switch (request->tcode) { 448 switch (request->tcode) {
@@ -434,11 +460,13 @@ static int ioctl_send_request(struct client *client, void *buffer)
434 break; 460 break;
435 default: 461 default:
436 ret = -EINVAL; 462 ret = -EINVAL;
437 goto err; 463 goto failed;
438 } 464 }
439 465
440 response->resource.release = release_transaction; 466 response->resource.release = release_transaction;
441 add_client_resource(client, &response->resource); 467 ret = add_client_resource(client, &response->resource, GFP_KERNEL);
468 if (ret < 0)
469 goto failed;
442 470
443 fw_send_request(device->card, &response->transaction, 471 fw_send_request(device->card, &response->transaction,
444 request->tcode & 0x1f, 472 request->tcode & 0x1f,
@@ -453,7 +481,7 @@ static int ioctl_send_request(struct client *client, void *buffer)
453 return sizeof(request) + request->length; 481 return sizeof(request) + request->length;
454 else 482 else
455 return sizeof(request); 483 return sizeof(request);
456 err: 484 failed:
457 kfree(response); 485 kfree(response);
458 486
459 return ret; 487 return ret;
@@ -500,22 +528,21 @@ handle_request(struct fw_card *card, struct fw_request *r,
500 struct request *request; 528 struct request *request;
501 struct request_event *e; 529 struct request_event *e;
502 struct client *client = handler->client; 530 struct client *client = handler->client;
531 int ret;
503 532
504 request = kmalloc(sizeof(*request), GFP_ATOMIC); 533 request = kmalloc(sizeof(*request), GFP_ATOMIC);
505 e = kmalloc(sizeof(*e), GFP_ATOMIC); 534 e = kmalloc(sizeof(*e), GFP_ATOMIC);
506 if (request == NULL || e == NULL) { 535 if (request == NULL || e == NULL)
507 kfree(request); 536 goto failed;
508 kfree(e);
509 fw_send_response(card, r, RCODE_CONFLICT_ERROR);
510 return;
511 }
512 537
513 request->request = r; 538 request->request = r;
514 request->data = payload; 539 request->data = payload;
515 request->length = length; 540 request->length = length;
516 541
517 request->resource.release = release_request; 542 request->resource.release = release_request;
518 add_client_resource(client, &request->resource); 543 ret = add_client_resource(client, &request->resource, GFP_ATOMIC);
544 if (ret < 0)
545 goto failed;
519 546
520 e->request.type = FW_CDEV_EVENT_REQUEST; 547 e->request.type = FW_CDEV_EVENT_REQUEST;
521 e->request.tcode = tcode; 548 e->request.tcode = tcode;
@@ -526,6 +553,12 @@ handle_request(struct fw_card *card, struct fw_request *r,
526 553
527 queue_event(client, &e->event, 554 queue_event(client, &e->event,
528 &e->request, sizeof(e->request), payload, length); 555 &e->request, sizeof(e->request), payload, length);
556 return;
557
558 failed:
559 kfree(request);
560 kfree(e);
561 fw_send_response(card, r, RCODE_CONFLICT_ERROR);
529} 562}
530 563
531static void 564static void
@@ -544,6 +577,7 @@ static int ioctl_allocate(struct client *client, void *buffer)
544 struct fw_cdev_allocate *request = buffer; 577 struct fw_cdev_allocate *request = buffer;
545 struct address_handler *handler; 578 struct address_handler *handler;
546 struct fw_address_region region; 579 struct fw_address_region region;
580 int ret;
547 581
548 handler = kmalloc(sizeof(*handler), GFP_KERNEL); 582 handler = kmalloc(sizeof(*handler), GFP_KERNEL);
549 if (handler == NULL) 583 if (handler == NULL)
@@ -563,7 +597,11 @@ static int ioctl_allocate(struct client *client, void *buffer)
563 } 597 }
564 598
565 handler->resource.release = release_address_handler; 599 handler->resource.release = release_address_handler;
566 add_client_resource(client, &handler->resource); 600 ret = add_client_resource(client, &handler->resource, GFP_KERNEL);
601 if (ret < 0) {
602 release_address_handler(client, &handler->resource);
603 return ret;
604 }
567 request->handle = handler->resource.handle; 605 request->handle = handler->resource.handle;
568 606
569 return 0; 607 return 0;
@@ -573,7 +611,8 @@ static int ioctl_deallocate(struct client *client, void *buffer)
573{ 611{
574 struct fw_cdev_deallocate *request = buffer; 612 struct fw_cdev_deallocate *request = buffer;
575 613
576 return release_client_resource(client, request->handle, NULL); 614 return release_client_resource(client, request->handle,
615 release_address_handler, NULL);
577} 616}
578 617
579static int ioctl_send_response(struct client *client, void *buffer) 618static int ioctl_send_response(struct client *client, void *buffer)
@@ -582,8 +621,10 @@ static int ioctl_send_response(struct client *client, void *buffer)
582 struct client_resource *resource; 621 struct client_resource *resource;
583 struct request *r; 622 struct request *r;
584 623
585 if (release_client_resource(client, request->handle, &resource) < 0) 624 if (release_client_resource(client, request->handle,
625 release_request, &resource) < 0)
586 return -EINVAL; 626 return -EINVAL;
627
587 r = container_of(resource, struct request, resource); 628 r = container_of(resource, struct request, resource);
588 if (request->length < r->length) 629 if (request->length < r->length)
589 r->length = request->length; 630 r->length = request->length;
@@ -626,7 +667,7 @@ static int ioctl_add_descriptor(struct client *client, void *buffer)
626{ 667{
627 struct fw_cdev_add_descriptor *request = buffer; 668 struct fw_cdev_add_descriptor *request = buffer;
628 struct descriptor *descriptor; 669 struct descriptor *descriptor;
629 int retval; 670 int ret;
630 671
631 if (request->length > 256) 672 if (request->length > 256)
632 return -EINVAL; 673 return -EINVAL;
@@ -638,8 +679,8 @@ static int ioctl_add_descriptor(struct client *client, void *buffer)
638 679
639 if (copy_from_user(descriptor->data, 680 if (copy_from_user(descriptor->data,
640 u64_to_uptr(request->data), request->length * 4)) { 681 u64_to_uptr(request->data), request->length * 4)) {
641 kfree(descriptor); 682 ret = -EFAULT;
642 return -EFAULT; 683 goto failed;
643 } 684 }
644 685
645 descriptor->d.length = request->length; 686 descriptor->d.length = request->length;
@@ -647,24 +688,31 @@ static int ioctl_add_descriptor(struct client *client, void *buffer)
647 descriptor->d.key = request->key; 688 descriptor->d.key = request->key;
648 descriptor->d.data = descriptor->data; 689 descriptor->d.data = descriptor->data;
649 690
650 retval = fw_core_add_descriptor(&descriptor->d); 691 ret = fw_core_add_descriptor(&descriptor->d);
651 if (retval < 0) { 692 if (ret < 0)
652 kfree(descriptor); 693 goto failed;
653 return retval;
654 }
655 694
656 descriptor->resource.release = release_descriptor; 695 descriptor->resource.release = release_descriptor;
657 add_client_resource(client, &descriptor->resource); 696 ret = add_client_resource(client, &descriptor->resource, GFP_KERNEL);
697 if (ret < 0) {
698 fw_core_remove_descriptor(&descriptor->d);
699 goto failed;
700 }
658 request->handle = descriptor->resource.handle; 701 request->handle = descriptor->resource.handle;
659 702
660 return 0; 703 return 0;
704 failed:
705 kfree(descriptor);
706
707 return ret;
661} 708}
662 709
663static int ioctl_remove_descriptor(struct client *client, void *buffer) 710static int ioctl_remove_descriptor(struct client *client, void *buffer)
664{ 711{
665 struct fw_cdev_remove_descriptor *request = buffer; 712 struct fw_cdev_remove_descriptor *request = buffer;
666 713
667 return release_client_resource(client, request->handle, NULL); 714 return release_client_resource(client, request->handle,
715 release_descriptor, NULL);
668} 716}
669 717
670static void 718static void
@@ -1003,11 +1051,21 @@ static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma)
1003 return retval; 1051 return retval;
1004} 1052}
1005 1053
1054static int shutdown_resource(int id, void *p, void *data)
1055{
1056 struct client_resource *r = p;
1057 struct client *client = data;
1058
1059 r->release(client, r);
1060
1061 return 0;
1062}
1063
1006static int fw_device_op_release(struct inode *inode, struct file *file) 1064static int fw_device_op_release(struct inode *inode, struct file *file)
1007{ 1065{
1008 struct client *client = file->private_data; 1066 struct client *client = file->private_data;
1009 struct event *e, *next_e; 1067 struct event *e, *next_e;
1010 struct client_resource *r, *next_r; 1068 unsigned long flags;
1011 1069
1012 mutex_lock(&client->device->client_list_mutex); 1070 mutex_lock(&client->device->client_list_mutex);
1013 list_del(&client->link); 1071 list_del(&client->link);
@@ -1019,17 +1077,22 @@ static int fw_device_op_release(struct inode *inode, struct file *file)
1019 if (client->iso_context) 1077 if (client->iso_context)
1020 fw_iso_context_destroy(client->iso_context); 1078 fw_iso_context_destroy(client->iso_context);
1021 1079
1022 list_for_each_entry_safe(r, next_r, &client->resource_list, link) 1080 /* Freeze client->resource_idr and client->event_list */
1023 r->release(client, r); 1081 spin_lock_irqsave(&client->lock, flags);
1082 client->in_shutdown = true;
1083 spin_unlock_irqrestore(&client->lock, flags);
1024 1084
1025 /* 1085 idr_for_each(&client->resource_idr, shutdown_resource, client);
1026 * FIXME: We should wait for the async tasklets to stop 1086 idr_remove_all(&client->resource_idr);
1027 * running before freeing the memory. 1087 idr_destroy(&client->resource_idr);
1028 */
1029 1088
1030 list_for_each_entry_safe(e, next_e, &client->event_list, link) 1089 list_for_each_entry_safe(e, next_e, &client->event_list, link)
1031 kfree(e); 1090 kfree(e);
1032 1091
1092 /*
1093 * FIXME: client should be reference-counted. It's extremely unlikely
1094 * but there may still be transactions being completed at this point.
1095 */
1033 fw_device_put(client->device); 1096 fw_device_put(client->device);
1034 kfree(client); 1097 kfree(client);
1035 1098