firewire: core: use separate timeout for each transaction

Using a single timeout for all transaction that need to be flushed does
not work if the submission of new transactions can defer the timeout
indefinitely into the future.  We need to have timeouts that do not
change due to other transactions; the simplest way to do this is with a
separate timer for each transaction.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> (+ one lockdep annotation)
diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c
index 9016698..fdc33ff 100644
--- a/drivers/firewire/core-transaction.c
+++ b/drivers/firewire/core-transaction.c
@@ -81,7 +81,7 @@
 	spin_lock_irqsave(&card->lock, flags);
 	list_for_each_entry(t, &card->transaction_list, link) {
 		if (t == transaction) {
-			list_del(&t->link);
+			list_del_init(&t->link);
 			card->tlabel_mask &= ~(1ULL << t->tlabel);
 			break;
 		}
@@ -89,6 +89,7 @@
 	spin_unlock_irqrestore(&card->lock, flags);
 
 	if (&t->link != &card->transaction_list) {
+		del_timer_sync(&t->split_timeout_timer);
 		t->callback(card, rcode, NULL, 0, t->callback_data);
 		return 0;
 	}
@@ -121,6 +122,31 @@
 }
 EXPORT_SYMBOL(fw_cancel_transaction);
 
+static void split_transaction_timeout_callback(unsigned long data)
+{
+	struct fw_transaction *t = (struct fw_transaction *)data;
+	struct fw_card *card = t->card;
+	unsigned long flags;
+
+	spin_lock_irqsave(&card->lock, flags);
+	if (list_empty(&t->link)) {
+		spin_unlock_irqrestore(&card->lock, flags);
+		return;
+	}
+	list_del(&t->link);
+	card->tlabel_mask &= ~(1ULL << t->tlabel);
+	spin_unlock_irqrestore(&card->lock, flags);
+
+	card->driver->cancel_packet(card, &t->packet);
+
+	/*
+	 * At this point cancel_packet will never call the transaction
+	 * callback, since we just took the transaction out of the list.
+	 * So do it here.
+	 */
+	t->callback(card, RCODE_CANCELLED, NULL, 0, t->callback_data);
+}
+
 static void transmit_complete_callback(struct fw_packet *packet,
 				       struct fw_card *card, int status)
 {
@@ -294,13 +320,6 @@
 	int tlabel;
 
 	/*
-	 * Bump the flush timer up 100ms first of all so we
-	 * don't race with a flush timer callback.
-	 */
-
-	mod_timer(&card->flush_timer, jiffies + DIV_ROUND_UP(HZ, 10));
-
-	/*
 	 * Allocate tlabel from the bitmap and put the transaction on
 	 * the list while holding the card spinlock.
 	 */
@@ -316,6 +335,11 @@
 
 	t->node_id = destination_id;
 	t->tlabel = tlabel;
+	t->card = card;
+	setup_timer(&t->split_timeout_timer,
+		    split_transaction_timeout_callback, (unsigned long)t);
+	/* FIXME: start this timer later, relative to t->timestamp */
+	mod_timer(&t->split_timeout_timer, jiffies + DIV_ROUND_UP(HZ, 10));
 	t->callback = callback;
 	t->callback_data = callback_data;
 
@@ -361,11 +385,13 @@
 	struct transaction_callback_data d;
 	struct fw_transaction t;
 
+	init_timer_on_stack(&t.split_timeout_timer);
 	init_completion(&d.done);
 	d.payload = payload;
 	fw_send_request(card, &t, tcode, destination_id, generation, speed,
 			offset, payload, length, transaction_callback, &d);
 	wait_for_completion(&d.done);
+	destroy_timer_on_stack(&t.split_timeout_timer);
 
 	return d.rcode;
 }
@@ -408,30 +434,6 @@
 	mutex_unlock(&phy_config_mutex);
 }
 
-void fw_flush_transactions(struct fw_card *card)
-{
-	struct fw_transaction *t, *next;
-	struct list_head list;
-	unsigned long flags;
-
-	INIT_LIST_HEAD(&list);
-	spin_lock_irqsave(&card->lock, flags);
-	list_splice_init(&card->transaction_list, &list);
-	card->tlabel_mask = 0;
-	spin_unlock_irqrestore(&card->lock, flags);
-
-	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 *lookup_overlapping_address_handler(
 	struct list_head *list, unsigned long long offset, size_t length)
 {
@@ -841,7 +843,7 @@
 	spin_lock_irqsave(&card->lock, flags);
 	list_for_each_entry(t, &card->transaction_list, link) {
 		if (t->node_id == source && t->tlabel == tlabel) {
-			list_del(&t->link);
+			list_del_init(&t->link);
 			card->tlabel_mask &= ~(1ULL << t->tlabel);
 			break;
 		}
@@ -883,6 +885,8 @@
 		break;
 	}
 
+	del_timer_sync(&t->split_timeout_timer);
+
 	/*
 	 * The response handler may be executed while the request handler
 	 * is still pending.  Cancel the request handler.