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>
diff --git a/drivers/firewire/fw-transaction.c b/drivers/firewire/fw-transaction.c
index fb3b77e..5394569 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 @@
 
 	packet->speed = speed;
 	packet->generation = generation;
+	packet->ack = 0;
 }
 
 /**
@@ -298,8 +331,14 @@
 	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 @@
 	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;