diff options
author | Kristian Høgsberg <krh@redhat.com> | 2007-02-06 14:49:32 -0500 |
---|---|---|
committer | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2007-03-09 16:02:51 -0500 |
commit | 730c32f58ba81b3a4fe6d19c7d9e9829dd96d363 (patch) | |
tree | 79149d002b095ca27582d4d7ef00c8eefec67170 /drivers/firewire/fw-transaction.c | |
parent | 72e318e07e1fa9840bfdd5788421fc6dc51a93de (diff) |
firewire: Implement proper transaction cancelation.
Drivers such as fw-sbp2 had no way to properly cancel in-progress
transactions, which could leave a pending transaction or an unset
packet in the low-level queues after kfree'ing the containing
structure. fw_cancel_transaction() lets drivers cancel a submitted
transaction.
Signed-off-by: Kristian Høgsberg <krh@redhat.com>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Diffstat (limited to 'drivers/firewire/fw-transaction.c')
-rw-r--r-- | drivers/firewire/fw-transaction.c | 54 |
1 files changed, 47 insertions, 7 deletions
diff --git a/drivers/firewire/fw-transaction.c b/drivers/firewire/fw-transaction.c index fb3b77e1bb2d..5394569a1c89 100644 --- a/drivers/firewire/fw-transaction.c +++ b/drivers/firewire/fw-transaction.c | |||
@@ -59,20 +59,52 @@ | |||
59 | #define phy_config_root_id(node_id) ((((node_id) & 0x3f) << 24) | (1 << 23)) | 59 | #define phy_config_root_id(node_id) ((((node_id) & 0x3f) << 24) | (1 << 23)) |
60 | #define phy_identifier(id) ((id) << 30) | 60 | #define phy_identifier(id) ((id) << 30) |
61 | 61 | ||
62 | static void | 62 | static int |
63 | close_transaction(struct fw_transaction *t, struct fw_card *card, int rcode, | 63 | close_transaction(struct fw_transaction *transaction, |
64 | u32 * payload, size_t length) | 64 | struct fw_card *card, int rcode, |
65 | u32 *payload, size_t length) | ||
65 | { | 66 | { |
67 | struct fw_transaction *t; | ||
66 | unsigned long flags; | 68 | unsigned long flags; |
67 | 69 | ||
68 | spin_lock_irqsave(&card->lock, flags); | 70 | spin_lock_irqsave(&card->lock, flags); |
69 | card->tlabel_mask &= ~(1 << t->tlabel); | 71 | list_for_each_entry(t, &card->transaction_list, link) { |
70 | list_del(&t->link); | 72 | if (t == transaction) { |
73 | list_del(&t->link); | ||
74 | card->tlabel_mask &= ~(1 << t->tlabel); | ||
75 | break; | ||
76 | } | ||
77 | } | ||
71 | spin_unlock_irqrestore(&card->lock, flags); | 78 | spin_unlock_irqrestore(&card->lock, flags); |
72 | 79 | ||
73 | t->callback(card, rcode, payload, length, t->callback_data); | 80 | if (&t->link != &card->transaction_list) { |
81 | t->callback(card, rcode, payload, length, t->callback_data); | ||
82 | return 0; | ||
83 | } | ||
84 | |||
85 | return -ENOENT; | ||
74 | } | 86 | } |
75 | 87 | ||
88 | /* Only valid for transactions that are potentially pending (ie have | ||
89 | * been sent). */ | ||
90 | int | ||
91 | fw_cancel_transaction(struct fw_card *card, | ||
92 | struct fw_transaction *transaction) | ||
93 | { | ||
94 | /* Cancel the packet transmission if it's still queued. That | ||
95 | * will call the packet transmission callback which cancels | ||
96 | * the transaction. */ | ||
97 | |||
98 | if (card->driver->cancel_packet(card, &transaction->packet) == 0) | ||
99 | return 0; | ||
100 | |||
101 | /* If the request packet has already been sent, we need to see | ||
102 | * if the transaction is still pending and remove it in that case. */ | ||
103 | |||
104 | return close_transaction(transaction, card, RCODE_CANCELLED, NULL, 0); | ||
105 | } | ||
106 | EXPORT_SYMBOL(fw_cancel_transaction); | ||
107 | |||
76 | static void | 108 | static void |
77 | transmit_complete_callback(struct fw_packet *packet, | 109 | transmit_complete_callback(struct fw_packet *packet, |
78 | struct fw_card *card, int status) | 110 | struct fw_card *card, int status) |
@@ -162,6 +194,7 @@ fw_fill_request(struct fw_packet *packet, int tcode, int tlabel, | |||
162 | 194 | ||
163 | packet->speed = speed; | 195 | packet->speed = speed; |
164 | packet->generation = generation; | 196 | packet->generation = generation; |
197 | packet->ack = 0; | ||
165 | } | 198 | } |
166 | 199 | ||
167 | /** | 200 | /** |
@@ -298,8 +331,14 @@ void fw_flush_transactions(struct fw_card *card) | |||
298 | card->tlabel_mask = 0; | 331 | card->tlabel_mask = 0; |
299 | spin_unlock_irqrestore(&card->lock, flags); | 332 | spin_unlock_irqrestore(&card->lock, flags); |
300 | 333 | ||
301 | list_for_each_entry_safe(t, next, &list, link) | 334 | list_for_each_entry_safe(t, next, &list, link) { |
335 | card->driver->cancel_packet(card, &t->packet); | ||
336 | |||
337 | /* At this point cancel_packet will never call the | ||
338 | * transaction callback, since we just took all the | ||
339 | * transactions out of the list. So do it here.*/ | ||
302 | t->callback(card, RCODE_CANCELLED, NULL, 0, t->callback_data); | 340 | t->callback(card, RCODE_CANCELLED, NULL, 0, t->callback_data); |
341 | } | ||
303 | } | 342 | } |
304 | 343 | ||
305 | static struct fw_address_handler * | 344 | static struct fw_address_handler * |
@@ -531,6 +570,7 @@ allocate_request(struct fw_packet *p) | |||
531 | request->response.speed = p->speed; | 570 | request->response.speed = p->speed; |
532 | request->response.timestamp = t; | 571 | request->response.timestamp = t; |
533 | request->response.generation = p->generation; | 572 | request->response.generation = p->generation; |
573 | request->response.ack = 0; | ||
534 | request->response.callback = free_response_callback; | 574 | request->response.callback = free_response_callback; |
535 | request->ack = p->ack; | 575 | request->ack = p->ack; |
536 | request->length = length; | 576 | request->length = length; |