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>
This commit is contained in:

committed by
Stefan Richter

parent
72e318e07e
commit
730c32f58b
@@ -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;
|
||||
|
Reference in New Issue
Block a user