diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/firewire/core-cdev.c | 42 |
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, | |||
520 | static void release_transaction(struct client *client, | 522 | static 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 | ||
529 | static void complete_transaction(struct fw_card *card, int rcode, | 527 | static 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 | ||
1668 | static 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 | |||
1675 | static 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 | |||
1675 | static int shutdown_resource(int id, void *p, void *data) | 1687 | static 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); |