aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firewire
diff options
context:
space:
mode:
authorClemens Ladisch <clemens@ladisch.de>2011-01-10 11:28:39 -0500
committerStefan Richter <stefanr@s5r6.in-berlin.de>2011-01-23 06:31:00 -0500
commit5a5e62da9be255439e8ce59f96828775b7b33374 (patch)
tree61c3154c6574f52b0b2981ad7698275fc0693c5b /drivers/firewire
parent3e204dfcaff0e7f6c4d9873fb8c9d948ec5ab2da (diff)
firewire: cdev: always wait for outbound transactions to complete
We must not use fw_cancel_transaction() because it cannot correctly abort still-active transactions. The only place in core-cdev where this matters is when the file is released. Instead of trying to abort the transactions, we wait for them to complete normally, i.e., until all outbound transaction resources have been removed from the IDR tree. Signed-off-by: Clemens Ladisch <clemens@ladisch.de> Signed-off-by: "Stefan Richter" <stefanr@s5r6.in-berlin.de>
Diffstat (limited to 'drivers/firewire')
-rw-r--r--drivers/firewire/core-cdev.c42
1 files changed, 28 insertions, 14 deletions
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c
index 4434f7ca11d5..5485c0877b81 100644
--- a/drivers/firewire/core-cdev.c
+++ b/drivers/firewire/core-cdev.c
@@ -64,6 +64,7 @@ struct client {
64 struct idr resource_idr; 64 struct idr resource_idr;
65 struct list_head event_list; 65 struct list_head event_list;
66 wait_queue_head_t wait; 66 wait_queue_head_t wait;
67 wait_queue_head_t tx_flush_wait;
67 u64 bus_reset_closure; 68 u64 bus_reset_closure;
68 69
69 struct fw_iso_context *iso_context; 70 struct fw_iso_context *iso_context;
@@ -251,6 +252,7 @@ static int fw_device_op_open(struct inode *inode, struct file *file)
251 idr_init(&client->resource_idr); 252 idr_init(&client->resource_idr);
252 INIT_LIST_HEAD(&client->event_list); 253 INIT_LIST_HEAD(&client->event_list);
253 init_waitqueue_head(&client->wait); 254 init_waitqueue_head(&client->wait);
255 init_waitqueue_head(&client->tx_flush_wait);
254 INIT_LIST_HEAD(&client->phy_receiver_link); 256 INIT_LIST_HEAD(&client->phy_receiver_link);
255 kref_init(&client->kref); 257 kref_init(&client->kref);
256 258
@@ -520,10 +522,6 @@ static int release_client_resource(struct client *client, u32 handle,
520static void release_transaction(struct client *client, 522static void release_transaction(struct client *client,
521 struct client_resource *resource) 523 struct client_resource *resource)
522{ 524{
523 struct outbound_transaction_resource *r = container_of(resource,
524 struct outbound_transaction_resource, resource);
525
526 fw_cancel_transaction(client->device->card, &r->transaction);
527} 525}
528 526
529static void complete_transaction(struct fw_card *card, int rcode, 527static void complete_transaction(struct fw_card *card, int rcode,
@@ -540,16 +538,9 @@ static void complete_transaction(struct fw_card *card, int rcode,
540 memcpy(rsp->data, payload, rsp->length); 538 memcpy(rsp->data, payload, rsp->length);
541 539
542 spin_lock_irqsave(&client->lock, flags); 540 spin_lock_irqsave(&client->lock, flags);
543 /* 541 idr_remove(&client->resource_idr, e->r.resource.handle);
544 * 1. If called while in shutdown, the idr tree must be left untouched. 542 if (client->in_shutdown)
545 * The idr handle will be removed and the client reference will be 543 wake_up(&client->tx_flush_wait);
546 * dropped later.
547 */
548 if (!client->in_shutdown) {
549 idr_remove(&client->resource_idr, e->r.resource.handle);
550 /* Drop the idr's reference */
551 client_put(client);
552 }
553 spin_unlock_irqrestore(&client->lock, flags); 544 spin_unlock_irqrestore(&client->lock, flags);
554 545
555 rsp->type = FW_CDEV_EVENT_RESPONSE; 546 rsp->type = FW_CDEV_EVENT_RESPONSE;
@@ -569,6 +560,8 @@ static void complete_transaction(struct fw_card *card, int rcode,
569 queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length, 560 queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length,
570 NULL, 0); 561 NULL, 0);
571 562
563 /* Drop the idr's reference */
564 client_put(client);
572 /* Drop the transaction callback's reference */ 565 /* Drop the transaction callback's reference */
573 client_put(client); 566 client_put(client);
574} 567}
@@ -1672,6 +1665,25 @@ static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma)
1672 return ret; 1665 return ret;
1673} 1666}
1674 1667
1668static int is_outbound_transaction_resource(int id, void *p, void *data)
1669{
1670 struct client_resource *resource = p;
1671
1672 return resource->release == release_transaction;
1673}
1674
1675static int has_outbound_transactions(struct client *client)
1676{
1677 int ret;
1678
1679 spin_lock_irq(&client->lock);
1680 ret = idr_for_each(&client->resource_idr,
1681 is_outbound_transaction_resource, NULL);
1682 spin_unlock_irq(&client->lock);
1683
1684 return ret;
1685}
1686
1675static int shutdown_resource(int id, void *p, void *data) 1687static int shutdown_resource(int id, void *p, void *data)
1676{ 1688{
1677 struct client_resource *resource = p; 1689 struct client_resource *resource = p;
@@ -1707,6 +1719,8 @@ static int fw_device_op_release(struct inode *inode, struct file *file)
1707 client->in_shutdown = true; 1719 client->in_shutdown = true;
1708 spin_unlock_irq(&client->lock); 1720 spin_unlock_irq(&client->lock);
1709 1721
1722 wait_event(client->tx_flush_wait, !has_outbound_transactions(client));
1723
1710 idr_for_each(&client->resource_idr, shutdown_resource, client); 1724 idr_for_each(&client->resource_idr, shutdown_resource, client);
1711 idr_remove_all(&client->resource_idr); 1725 idr_remove_all(&client->resource_idr);
1712 idr_destroy(&client->resource_idr); 1726 idr_destroy(&client->resource_idr);