summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>2011-10-13 13:29:24 +0200
committerDavid S. Miller <davem@davemloft.net>2011-10-19 09:25:41 +0200
commit73033c987a8bd0b080509063bb7c130b8941ad73 (patch)
tree3fc3b0f7160c9f9659eed18721b6770468def367
parentcaif-hsi: Fixing a race condition in the caif_hsi code (diff)
downloadlinux-73033c987a8bd0b080509063bb7c130b8941ad73.tar.xz
linux-73033c987a8bd0b080509063bb7c130b8941ad73.zip
caif-hsi: Fix for wakeup condition problem
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>
-rw-r--r--drivers/net/caif/caif_hsi.c10
1 files changed, 7 insertions, 3 deletions
diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c
index 36da27b50114..82c4d6ca2d3f 100644
--- a/drivers/net/caif/caif_hsi.c
+++ b/drivers/net/caif/caif_hsi.c
@@ -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;