diff options
author | Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> | 2011-05-07 21:55:58 +0200 |
---|---|---|
committer | Ziyann <jaraidaniel@gmail.com> | 2014-10-01 12:55:58 +0200 |
commit | c9a158f1ee4960bfa385705c93b104ad1305a0bc (patch) | |
tree | f46455961a7ff8e20e7225475bdacef3a758aaa0 /drivers/net/caif | |
parent | 2225d6b5e3f7774ac9861d32fd3f52c4febc670a (diff) | |
download | kernel_samsung_tuna-c9a158f1ee4960bfa385705c93b104ad1305a0bc.zip kernel_samsung_tuna-c9a158f1ee4960bfa385705c93b104ad1305a0bc.tar.gz kernel_samsung_tuna-c9a158f1ee4960bfa385705c93b104ad1305a0bc.tar.bz2 |
CAIF: HSI: Fixing a kernel panic at driver removal.
add_timer was called when timer was already started. Same issue on
TX errors. Usage of add_timer is completly replaced by mod_timer().
A flag for shutdown is introduced. No timers nor work items are
scheduled when driver is shutting down.
Wakelock is always released on exit now.
Change-Id: Iad689d9fe322d40cfea09789c4cccc5134ee06f9
Signed-off-by: Erwan Bracq <erwan.bracq@stericsson.com>
Signed-off-by: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>
Diffstat (limited to 'drivers/net/caif')
-rw-r--r-- | drivers/net/caif/caif_hsi.c | 128 |
1 files changed, 87 insertions, 41 deletions
diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c index f298bb8..f08cd56 100644 --- a/drivers/net/caif/caif_hsi.c +++ b/drivers/net/caif/caif_hsi.c @@ -77,7 +77,8 @@ static void cfhsi_inactivity_tout(unsigned long arg) __func__); /* Schedule power down work queue. */ - queue_work(cfhsi->wq, &cfhsi->wake_down_work); + if (!test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) + queue_work(cfhsi->wq, &cfhsi->wake_down_work); } @@ -98,8 +99,8 @@ static void cfhsi_abort_tx(struct cfhsi *cfhsi) kfree_skb(skb); } cfhsi->tx_state = CFHSI_TX_STATE_IDLE; - cfhsi->timer.expires = jiffies + CFHSI_INACTIVITY_TOUT; - add_timer(&cfhsi->timer); + if (!test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) + mod_timer(&cfhsi->timer, jiffies + CFHSI_INACTIVITY_TOUT); spin_unlock_irqrestore(&cfhsi->lock, flags); } @@ -216,6 +217,9 @@ static void cfhsi_tx_done_work(struct work_struct *work) dev_dbg(&cfhsi->ndev->dev, "%s.\n", __func__); + if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) + return; + desc = (struct cfhsi_desc *)cfhsi->tx_buf; do { @@ -237,8 +241,8 @@ static void cfhsi_tx_done_work(struct work_struct *work) if (!skb) { cfhsi->tx_state = CFHSI_TX_STATE_IDLE; /* Start inactivity timer. */ - cfhsi->timer.expires = jiffies + CFHSI_INACTIVITY_TOUT; - add_timer(&cfhsi->timer); + mod_timer(&cfhsi->timer, + jiffies + CFHSI_INACTIVITY_TOUT); spin_unlock_irqrestore(&cfhsi->lock, flags); break; } @@ -266,6 +270,9 @@ static void cfhsi_tx_done_cb(struct cfhsi_drv *drv) dev_dbg(&cfhsi->ndev->dev, "%s.\n", __func__); + if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) + return; + queue_work(cfhsi->wq, &cfhsi->tx_done_work); } @@ -469,6 +476,9 @@ static void cfhsi_rx_done_work(struct work_struct *work) dev_dbg(&cfhsi->ndev->dev, "%s: Kick timer if pending.\n", __func__); + if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) + return; + /* Update inactivity timer if pending. */ mod_timer_pending(&cfhsi->timer, jiffies + CFHSI_INACTIVITY_TOUT); @@ -531,6 +541,9 @@ static void cfhsi_rx_done_cb(struct cfhsi_drv *drv) dev_dbg(&cfhsi->ndev->dev, "%s.\n", __func__); + if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) + return; + set_bit(CFHSI_PENDING_RX, &cfhsi->bits); queue_work(cfhsi->wq, &cfhsi->rx_done_work); } @@ -545,6 +558,9 @@ static void cfhsi_wake_up(struct work_struct *work) cfhsi = container_of(work, struct cfhsi, wake_up_work); + if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) + return; + if (unlikely(test_bit(CFHSI_AWAKE, &cfhsi->bits))) { /* It happenes when wakeup is requested by * both ends at the same time. */ @@ -554,7 +570,7 @@ static void cfhsi_wake_up(struct work_struct *work) #ifdef CONFIG_WAKELOCK /* Hold system wake lock */ - if (!unlikely(test_and_clear_bit(CFHSI_WAKELOCK_HELD, &cfhsi->bits))) + if (!unlikely(test_bit(CFHSI_WAKELOCK_HELD, &cfhsi->bits))) wake_lock(&cfhsi->link_wakelock); #endif @@ -622,8 +638,8 @@ static void cfhsi_wake_up(struct work_struct *work) dev_dbg(&cfhsi->ndev->dev, "%s: Peer wake, start timer.\n", __func__); /* Start inactivity timer. */ - cfhsi->timer.expires = jiffies + CFHSI_INACTIVITY_TOUT; - add_timer(&cfhsi->timer); + mod_timer(&cfhsi->timer, + jiffies + CFHSI_INACTIVITY_TOUT); spin_unlock_irqrestore(&cfhsi->lock, flags); return; } @@ -662,6 +678,9 @@ static void cfhsi_wake_down(struct work_struct *work) dev_dbg(&cfhsi->ndev->dev, "%s.\n", __func__); + if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) + return; + /* Check if there is something in FIFO. */ if (WARN_ON(cfhsi->dev->cfhsi_fifo_occupancy(cfhsi->dev, &fifo_occupancy))) @@ -673,8 +692,8 @@ static void cfhsi_wake_down(struct work_struct *work) "%s: %d words in RX FIFO, restart timer.\n", __func__, fifo_occupancy); spin_lock_irqsave(&cfhsi->lock, flags); - cfhsi->timer.expires = jiffies + CFHSI_INACTIVITY_TOUT; - add_timer(&cfhsi->timer); + mod_timer(&cfhsi->timer, + jiffies + CFHSI_INACTIVITY_TOUT); spin_unlock_irqrestore(&cfhsi->lock, flags); return; } @@ -716,7 +735,6 @@ static void cfhsi_wake_down(struct work_struct *work) dev_dbg(&cfhsi->ndev->dev, "%s: %d words in RX FIFO, wakeup forced.\n", __func__, fifo_occupancy); - set_bit(CFHSI_WAKELOCK_HELD, &cfhsi->bits); if (!test_and_set_bit(CFHSI_WAKE_UP, &cfhsi->bits)) queue_work(cfhsi->wq, &cfhsi->wake_up_work); } else { @@ -724,6 +742,7 @@ static void cfhsi_wake_down(struct work_struct *work) __func__); #ifdef CONFIG_WAKELOCK /* Release system wake lock */ + clear_bit(CFHSI_WAKELOCK_HELD, &cfhsi->bits); wake_unlock(&cfhsi->link_wakelock); #endif } @@ -739,6 +758,10 @@ static void cfhsi_wake_up_cb(struct cfhsi_drv *drv) set_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits); wake_up_interruptible(&cfhsi->wake_up_wait); + + if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) + return; + /* Schedule wake up work queue if the peer initiates. */ if (!test_and_set_bit(CFHSI_WAKE_UP, &cfhsi->bits)) queue_work(cfhsi->wq, &cfhsi->wake_up_work); @@ -773,6 +796,13 @@ static int cfhsi_xmit(struct sk_buff *skb, struct net_device *dev) skb_queue_tail(&cfhsi->qhead, skb); + /* Sanity check; xmit should not be called after unregister_netdev */ + if (WARN_ON(test_bit(CFHSI_SHUTDOWN, &cfhsi->bits))) { + spin_unlock_irqrestore(&cfhsi->lock, flags); + cfhsi_abort_tx(cfhsi); + return -EINVAL; + } + /* Send flow off if number of packets is above high water mark. */ if (!cfhsi->flow_off_sent && cfhsi->qhead.qlen > cfhsi->q_high_mark && @@ -999,6 +1029,42 @@ int cfhsi_probe(struct platform_device *pdev) return res; } +static void cfhsi_shutdown(struct cfhsi *cfhsi) +{ + /* Unregister the network device. */ + unregister_netdev(cfhsi->ndev); + + /* going to shutdown driver */ + set_bit(CFHSI_SHUTDOWN, &cfhsi->bits); + + /* Flush workqueue */ + flush_workqueue(cfhsi->wq); + + /* Delete timer if pending */ +#ifdef CONFIG_SMP + del_timer_sync(&cfhsi->timer); +#else + del_timer(&cfhsi->timer); +#endif /* CONFIG_SMP */ + + /* Flush again and destroy workqueue */ + destroy_workqueue(cfhsi->wq); + + /* Free buffers. */ + kfree(cfhsi->tx_buf); + kfree(cfhsi->rx_buf); + + /* Flush transmit queues. */ + cfhsi_abort_tx(cfhsi); + + /* Destroy wakelock. Will also release if it is held. */ +#ifdef CONFIG_WAKELOCK + if (test_bit(CFHSI_WAKELOCK_HELD, &cfhsi->bits)) + wake_unlock(&cfhsi->link_wakelock); + wake_lock_destroy(&cfhsi->link_wakelock); +#endif +} + int cfhsi_remove(struct platform_device *pdev) { struct list_head *list_node; @@ -1015,23 +1081,10 @@ int cfhsi_remove(struct platform_device *pdev) /* Remove from list. */ list_del(list_node); spin_unlock(&cfhsi_list_lock); - /* Delete inactivity timer if started. */ - /* Free buffers. */ - kfree(cfhsi->tx_buf); - kfree(cfhsi->rx_buf); - /* Flush transmit queues. */ - cfhsi_abort_tx(cfhsi); -#ifdef CONFIG_SMP - del_timer_sync(&cfhsi->timer); -#else - del_timer(&cfhsi->timer); -#endif /* CONFIG_SMP */ - /* Unregister the network device. */ - destroy_workqueue(cfhsi->wq); -#ifdef CONFIG_WAKELOCK - wake_lock_destroy(&cfhsi->link_wakelock); -#endif - unregister_netdev(cfhsi->ndev); + + /* Shutdown driver. */ + cfhsi_shutdown(cfhsi); + return 0; } } @@ -1057,24 +1110,17 @@ static void __exit cfhsi_exit_module(void) spin_lock(&cfhsi_list_lock); list_for_each_safe(list_node, n, &cfhsi_list) { cfhsi = list_entry(list_node, struct cfhsi, list); + /* Remove from list. */ list_del(list_node); spin_unlock(&cfhsi_list_lock); - /* Delete inactivity timer if started. */ + /* Notify device. */ platform_device_unregister(cfhsi->pdev); - /* Free buffers. */ - kfree(cfhsi->tx_buf); - kfree(cfhsi->rx_buf); - /* Flush transmit queues. */ - cfhsi_abort_tx(cfhsi); -#ifdef CONFIG_SMP - del_timer_sync(&cfhsi->timer); -#else - del_timer(&cfhsi->timer); -#endif /* CONFIG_SMP */ - /* Unregister the network device. */ - unregister_netdev(cfhsi->ndev); + + /* Shutdown driver. */ + cfhsi_shutdown(cfhsi); + spin_lock(&cfhsi_list_lock); } spin_unlock(&cfhsi_list_lock); |