caif-hsi: Fix for wakeup condition problem
authorDmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>
Thu, 13 Oct 2011 11:29:24 +0000 (11:29 +0000)
committerDavid S. Miller <davem@davemloft.net>
Wed, 19 Oct 2011 07:25:41 +0000 (03:25 -0400)
Under stressed conditions a race could happen when del_timer_sync() was called
from softirq context at the same time when mod_timer_pending() for the same
timer was called from the workqueue. This leaded to a state mismatch in the
CAIF HSI driver and following unexpected link wakeup procedure.

The fix puts del_timer_sync() and mod_timer_pending() calls under a spin lock
to protect against the race condition.

Signed-off-by: Sjur Brændeland <sjur.brandeland@stericsson.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/caif/caif_hsi.c

index 36da27b50114bfc0bbf5cc883be25e2512e27008..82c4d6ca2d3f919b93ca1e2bbe967074467b25c2 100644 (file)
@@ -551,7 +551,9 @@ static void cfhsi_rx_done_work(struct work_struct *work)
                return;
 
        /* Update inactivity timer if pending. */
+       spin_lock_bh(&cfhsi->lock);
        mod_timer_pending(&cfhsi->timer, jiffies + CFHSI_INACTIVITY_TOUT);
+       spin_unlock_bh(&cfhsi->lock);
 
        if (cfhsi->rx_state == CFHSI_RX_STATE_DESC) {
                desc_pld_len = cfhsi_rx_desc(desc, cfhsi);
@@ -866,10 +868,10 @@ static int cfhsi_xmit(struct sk_buff *skb, struct net_device *dev)
                start_xfer = 1;
        }
 
-       spin_unlock_bh(&cfhsi->lock);
-
-       if (!start_xfer)
+       if (!start_xfer) {
+               spin_unlock_bh(&cfhsi->lock);
                return 0;
+       }
 
        /* Delete inactivity timer if started. */
 #ifdef CONFIG_SMP
@@ -878,6 +880,8 @@ static int cfhsi_xmit(struct sk_buff *skb, struct net_device *dev)
        timer_active = del_timer(&cfhsi->timer);
 #endif /* CONFIG_SMP */
 
+       spin_unlock_bh(&cfhsi->lock);
+
        if (timer_active) {
                struct cfhsi_desc *desc = (struct cfhsi_desc *)cfhsi->tx_buf;
                int len;