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 22:02:51 +0100 |
commit | 730c32f58ba81b3a4fe6d19c7d9e9829dd96d363 (patch) | |
tree | 79149d002b095ca27582d4d7ef00c8eefec67170 /drivers/firewire/fw-transaction.c | |
parent | 72e318e07e1fa9840bfdd5788421fc6dc51a93de (diff) | |
download | linux-730c32f58ba81b3a4fe6d19c7d9e9829dd96d363.tar.bz2 |
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 @@ #define phy_config_root_id(node_id) ((((node_id) & 0x3f) << 24) | (1 << 23)) #define phy_identifier(id) ((id) << 30) -static void -close_transaction(struct fw_transaction *t, struct fw_card *card, int rcode, - u32 * payload, size_t length) +static int +close_transaction(struct fw_transaction *transaction, + struct fw_card *card, int rcode, + u32 *payload, size_t length) { + struct fw_transaction *t; unsigned long flags; spin_lock_irqsave(&card->lock, flags); - card->tlabel_mask &= ~(1 << t->tlabel); - list_del(&t->link); + list_for_each_entry(t, &card->transaction_list, link) { + if (t == transaction) { + list_del(&t->link); + card->tlabel_mask &= ~(1 << t->tlabel); + break; + } + } spin_unlock_irqrestore(&card->lock, flags); - t->callback(card, rcode, payload, length, t->callback_data); + if (&t->link != &card->transaction_list) { + t->callback(card, rcode, payload, length, t->callback_data); + return 0; + } + + return -ENOENT; } +/* Only valid for transactions that are potentially pending (ie have + * been sent). */ +int +fw_cancel_transaction(struct fw_card *card, + struct fw_transaction *transaction) +{ + /* Cancel the packet transmission if it's still queued. That + * will call the packet transmission callback which cancels + * the transaction. */ + + if (card->driver->cancel_packet(card, &transaction->packet) == 0) + return 0; + + /* If the request packet has already been sent, we need to see + * if the transaction is still pending and remove it in that case. */ + + return close_transaction(transaction, card, RCODE_CANCELLED, NULL, 0); +} +EXPORT_SYMBOL(fw_cancel_transaction); + static void transmit_complete_callback(struct fw_packet *packet, struct fw_card *card, int status) @@ -162,6 +194,7 @@ fw_fill_request(struct fw_packet *packet, int tcode, int tlabel, packet->speed = speed; packet->generation = generation; + packet->ack = 0; } /** @@ -298,8 +331,14 @@ void fw_flush_transactions(struct fw_card *card) card->tlabel_mask = 0; spin_unlock_irqrestore(&card->lock, flags); - list_for_each_entry_safe(t, next, &list, link) + list_for_each_entry_safe(t, next, &list, link) { + card->driver->cancel_packet(card, &t->packet); + + /* At this point cancel_packet will never call the + * transaction callback, since we just took all the + * transactions out of the list. So do it here.*/ t->callback(card, RCODE_CANCELLED, NULL, 0, t->callback_data); + } } static struct fw_address_handler * @@ -531,6 +570,7 @@ allocate_request(struct fw_packet *p) request->response.speed = p->speed; request->response.timestamp = t; request->response.generation = p->generation; + request->response.ack = 0; request->response.callback = free_response_callback; request->ack = p->ack; request->length = length; |