diff options
Diffstat (limited to 'drivers/firewire/core-cdev.c')
-rw-r--r-- | drivers/firewire/core-cdev.c | 68 |
1 files changed, 36 insertions, 32 deletions
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 14bb7b7b5dd7..b1c11775839c 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; |
@@ -140,7 +141,6 @@ struct iso_resource { | |||
140 | int generation; | 141 | int generation; |
141 | u64 channels; | 142 | u64 channels; |
142 | s32 bandwidth; | 143 | s32 bandwidth; |
143 | __be32 transaction_data[2]; | ||
144 | struct iso_resource_event *e_alloc, *e_dealloc; | 144 | struct iso_resource_event *e_alloc, *e_dealloc; |
145 | }; | 145 | }; |
146 | 146 | ||
@@ -149,7 +149,7 @@ static void release_iso_resource(struct client *, struct client_resource *); | |||
149 | static void schedule_iso_resource(struct iso_resource *r, unsigned long delay) | 149 | static void schedule_iso_resource(struct iso_resource *r, unsigned long delay) |
150 | { | 150 | { |
151 | client_get(r->client); | 151 | client_get(r->client); |
152 | if (!schedule_delayed_work(&r->work, delay)) | 152 | if (!queue_delayed_work(fw_workqueue, &r->work, delay)) |
153 | client_put(r->client); | 153 | client_put(r->client); |
154 | } | 154 | } |
155 | 155 | ||
@@ -251,6 +251,7 @@ static int fw_device_op_open(struct inode *inode, struct file *file) | |||
251 | idr_init(&client->resource_idr); | 251 | idr_init(&client->resource_idr); |
252 | INIT_LIST_HEAD(&client->event_list); | 252 | INIT_LIST_HEAD(&client->event_list); |
253 | init_waitqueue_head(&client->wait); | 253 | init_waitqueue_head(&client->wait); |
254 | init_waitqueue_head(&client->tx_flush_wait); | ||
254 | INIT_LIST_HEAD(&client->phy_receiver_link); | 255 | INIT_LIST_HEAD(&client->phy_receiver_link); |
255 | kref_init(&client->kref); | 256 | kref_init(&client->kref); |
256 | 257 | ||
@@ -520,10 +521,6 @@ static int release_client_resource(struct client *client, u32 handle, | |||
520 | static void release_transaction(struct client *client, | 521 | static void release_transaction(struct client *client, |
521 | struct client_resource *resource) | 522 | struct client_resource *resource) |
522 | { | 523 | { |
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 | } | 524 | } |
528 | 525 | ||
529 | static void complete_transaction(struct fw_card *card, int rcode, | 526 | static void complete_transaction(struct fw_card *card, int rcode, |
@@ -540,22 +537,9 @@ static void complete_transaction(struct fw_card *card, int rcode, | |||
540 | memcpy(rsp->data, payload, rsp->length); | 537 | memcpy(rsp->data, payload, rsp->length); |
541 | 538 | ||
542 | spin_lock_irqsave(&client->lock, flags); | 539 | spin_lock_irqsave(&client->lock, flags); |
543 | /* | 540 | idr_remove(&client->resource_idr, e->r.resource.handle); |
544 | * 1. If called while in shutdown, the idr tree must be left untouched. | 541 | if (client->in_shutdown) |
545 | * The idr handle will be removed and the client reference will be | 542 | wake_up(&client->tx_flush_wait); |
546 | * dropped later. | ||
547 | * 2. If the call chain was release_client_resource -> | ||
548 | * release_transaction -> complete_transaction (instead of a normal | ||
549 | * conclusion of the transaction), i.e. if this resource was already | ||
550 | * unregistered from the idr, the client reference will be dropped | ||
551 | * by release_client_resource and we must not drop it here. | ||
552 | */ | ||
553 | if (!client->in_shutdown && | ||
554 | idr_find(&client->resource_idr, e->r.resource.handle)) { | ||
555 | idr_remove(&client->resource_idr, e->r.resource.handle); | ||
556 | /* Drop the idr's reference */ | ||
557 | client_put(client); | ||
558 | } | ||
559 | spin_unlock_irqrestore(&client->lock, flags); | 543 | spin_unlock_irqrestore(&client->lock, flags); |
560 | 544 | ||
561 | rsp->type = FW_CDEV_EVENT_RESPONSE; | 545 | rsp->type = FW_CDEV_EVENT_RESPONSE; |
@@ -575,7 +559,7 @@ static void complete_transaction(struct fw_card *card, int rcode, | |||
575 | queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length, | 559 | queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length, |
576 | NULL, 0); | 560 | NULL, 0); |
577 | 561 | ||
578 | /* Drop the transaction callback's reference */ | 562 | /* Drop the idr's reference */ |
579 | client_put(client); | 563 | client_put(client); |
580 | } | 564 | } |
581 | 565 | ||
@@ -614,9 +598,6 @@ static int init_request(struct client *client, | |||
614 | if (ret < 0) | 598 | if (ret < 0) |
615 | goto failed; | 599 | goto failed; |
616 | 600 | ||
617 | /* Get a reference for the transaction callback */ | ||
618 | client_get(client); | ||
619 | |||
620 | fw_send_request(client->device->card, &e->r.transaction, | 601 | fw_send_request(client->device->card, &e->r.transaction, |
621 | request->tcode, destination_id, request->generation, | 602 | request->tcode, destination_id, request->generation, |
622 | speed, request->offset, e->response.data, | 603 | speed, request->offset, e->response.data, |
@@ -1126,6 +1107,7 @@ static int ioctl_queue_iso(struct client *client, union ioctl_arg *arg) | |||
1126 | payload += u.packet.payload_length; | 1107 | payload += u.packet.payload_length; |
1127 | count++; | 1108 | count++; |
1128 | } | 1109 | } |
1110 | fw_iso_context_queue_flush(ctx); | ||
1129 | 1111 | ||
1130 | a->size -= uptr_to_u64(p) - a->packets; | 1112 | a->size -= uptr_to_u64(p) - a->packets; |
1131 | a->packets = uptr_to_u64(p); | 1113 | a->packets = uptr_to_u64(p); |
@@ -1223,7 +1205,8 @@ static void iso_resource_work(struct work_struct *work) | |||
1223 | todo = r->todo; | 1205 | todo = r->todo; |
1224 | /* Allow 1000ms grace period for other reallocations. */ | 1206 | /* Allow 1000ms grace period for other reallocations. */ |
1225 | if (todo == ISO_RES_ALLOC && | 1207 | if (todo == ISO_RES_ALLOC && |
1226 | time_is_after_jiffies(client->device->card->reset_jiffies + HZ)) { | 1208 | time_before64(get_jiffies_64(), |
1209 | client->device->card->reset_jiffies + HZ)) { | ||
1227 | schedule_iso_resource(r, DIV_ROUND_UP(HZ, 3)); | 1210 | schedule_iso_resource(r, DIV_ROUND_UP(HZ, 3)); |
1228 | skip = true; | 1211 | skip = true; |
1229 | } else { | 1212 | } else { |
@@ -1246,8 +1229,7 @@ static void iso_resource_work(struct work_struct *work) | |||
1246 | r->channels, &channel, &bandwidth, | 1229 | r->channels, &channel, &bandwidth, |
1247 | todo == ISO_RES_ALLOC || | 1230 | todo == ISO_RES_ALLOC || |
1248 | todo == ISO_RES_REALLOC || | 1231 | todo == ISO_RES_REALLOC || |
1249 | todo == ISO_RES_ALLOC_ONCE, | 1232 | todo == ISO_RES_ALLOC_ONCE); |
1250 | r->transaction_data); | ||
1251 | /* | 1233 | /* |
1252 | * Is this generation outdated already? As long as this resource sticks | 1234 | * Is this generation outdated already? As long as this resource sticks |
1253 | * in the idr, it will be scheduled again for a newer generation or at | 1235 | * in the idr, it will be scheduled again for a newer generation or at |
@@ -1501,9 +1483,10 @@ static int ioctl_send_phy_packet(struct client *client, union ioctl_arg *arg) | |||
1501 | e->client = client; | 1483 | e->client = client; |
1502 | e->p.speed = SCODE_100; | 1484 | e->p.speed = SCODE_100; |
1503 | e->p.generation = a->generation; | 1485 | e->p.generation = a->generation; |
1504 | e->p.header[0] = a->data[0]; | 1486 | e->p.header[0] = TCODE_LINK_INTERNAL << 4; |
1505 | e->p.header[1] = a->data[1]; | 1487 | e->p.header[1] = a->data[0]; |
1506 | e->p.header_length = 8; | 1488 | e->p.header[2] = a->data[1]; |
1489 | e->p.header_length = 12; | ||
1507 | e->p.callback = outbound_phy_packet_callback; | 1490 | e->p.callback = outbound_phy_packet_callback; |
1508 | e->phy_packet.closure = a->closure; | 1491 | e->phy_packet.closure = a->closure; |
1509 | e->phy_packet.type = FW_CDEV_EVENT_PHY_PACKET_SENT; | 1492 | e->phy_packet.type = FW_CDEV_EVENT_PHY_PACKET_SENT; |
@@ -1677,6 +1660,25 @@ static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma) | |||
1677 | return ret; | 1660 | return ret; |
1678 | } | 1661 | } |
1679 | 1662 | ||
1663 | static int is_outbound_transaction_resource(int id, void *p, void *data) | ||
1664 | { | ||
1665 | struct client_resource *resource = p; | ||
1666 | |||
1667 | return resource->release == release_transaction; | ||
1668 | } | ||
1669 | |||
1670 | static int has_outbound_transactions(struct client *client) | ||
1671 | { | ||
1672 | int ret; | ||
1673 | |||
1674 | spin_lock_irq(&client->lock); | ||
1675 | ret = idr_for_each(&client->resource_idr, | ||
1676 | is_outbound_transaction_resource, NULL); | ||
1677 | spin_unlock_irq(&client->lock); | ||
1678 | |||
1679 | return ret; | ||
1680 | } | ||
1681 | |||
1680 | static int shutdown_resource(int id, void *p, void *data) | 1682 | static int shutdown_resource(int id, void *p, void *data) |
1681 | { | 1683 | { |
1682 | struct client_resource *resource = p; | 1684 | struct client_resource *resource = p; |
@@ -1712,6 +1714,8 @@ static int fw_device_op_release(struct inode *inode, struct file *file) | |||
1712 | client->in_shutdown = true; | 1714 | client->in_shutdown = true; |
1713 | spin_unlock_irq(&client->lock); | 1715 | spin_unlock_irq(&client->lock); |
1714 | 1716 | ||
1717 | wait_event(client->tx_flush_wait, !has_outbound_transactions(client)); | ||
1718 | |||
1715 | idr_for_each(&client->resource_idr, shutdown_resource, client); | 1719 | idr_for_each(&client->resource_idr, shutdown_resource, client); |
1716 | idr_remove_all(&client->resource_idr); | 1720 | idr_remove_all(&client->resource_idr); |
1717 | idr_destroy(&client->resource_idr); | 1721 | idr_destroy(&client->resource_idr); |