diff options
author | Mohamed Abbas <mabbas@linux.intel.com> | 2008-03-28 19:21:08 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-04-01 17:13:18 -0400 |
commit | 0359facc7b7a37fd1223ac60649c80cd8daeaf73 (patch) | |
tree | fcf6d571afa5a30dc893f853e20e6d81a3871b92 /drivers/net/wireless/iwlwifi/iwl4965-base.c | |
parent | ad97edd2f524940d524c26ae273a4eb23067a7c0 (diff) |
iwlwifi: fix race condition during driver unload
This patch fixed the OOPS when load the driver while rf-kill is on then
unload the driver right after load. a race condition caused the interupt
handler to schedule the tasklet which will run right after the driver pci_remove
causing invalid poiter OOPS.
Signed-off-by: Mohamed Abbas <mabbas@linux.intel.com>
Signed-off-by: Joonwoo Park <joonwpark81@gmail.com>
Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl4965-base.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl4965-base.c | 32 |
1 files changed, 30 insertions, 2 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl4965-base.c b/drivers/net/wireless/iwlwifi/iwl4965-base.c index 7f56565b4002..29e843108e9f 100644 --- a/drivers/net/wireless/iwlwifi/iwl4965-base.c +++ b/drivers/net/wireless/iwlwifi/iwl4965-base.c | |||
@@ -4285,6 +4285,14 @@ static void iwl4965_enable_interrupts(struct iwl_priv *priv) | |||
4285 | iwl_write32(priv, CSR_INT_MASK, CSR_INI_SET_MASK); | 4285 | iwl_write32(priv, CSR_INT_MASK, CSR_INI_SET_MASK); |
4286 | } | 4286 | } |
4287 | 4287 | ||
4288 | /* call this function to flush any scheduled tasklet */ | ||
4289 | static inline void iwl_synchronize_irq(struct iwl_priv *priv) | ||
4290 | { | ||
4291 | /* wait to make sure we flush pedding tasklet*/ | ||
4292 | synchronize_irq(priv->pci_dev->irq); | ||
4293 | tasklet_kill(&priv->irq_tasklet); | ||
4294 | } | ||
4295 | |||
4288 | static inline void iwl4965_disable_interrupts(struct iwl_priv *priv) | 4296 | static inline void iwl4965_disable_interrupts(struct iwl_priv *priv) |
4289 | { | 4297 | { |
4290 | clear_bit(STATUS_INT_ENABLED, &priv->status); | 4298 | clear_bit(STATUS_INT_ENABLED, &priv->status); |
@@ -4668,7 +4676,9 @@ static void iwl4965_irq_tasklet(struct iwl_priv *priv) | |||
4668 | } | 4676 | } |
4669 | 4677 | ||
4670 | /* Re-enable all interrupts */ | 4678 | /* Re-enable all interrupts */ |
4671 | iwl4965_enable_interrupts(priv); | 4679 | /* only Re-enable if diabled by irq */ |
4680 | if (test_bit(STATUS_INT_ENABLED, &priv->status)) | ||
4681 | iwl4965_enable_interrupts(priv); | ||
4672 | 4682 | ||
4673 | #ifdef CONFIG_IWLWIFI_DEBUG | 4683 | #ifdef CONFIG_IWLWIFI_DEBUG |
4674 | if (iwl_debug_level & (IWL_DL_ISR)) { | 4684 | if (iwl_debug_level & (IWL_DL_ISR)) { |
@@ -4733,7 +4743,9 @@ static irqreturn_t iwl4965_isr(int irq, void *data) | |||
4733 | 4743 | ||
4734 | none: | 4744 | none: |
4735 | /* re-enable interrupts here since we don't have anything to service. */ | 4745 | /* re-enable interrupts here since we don't have anything to service. */ |
4736 | iwl4965_enable_interrupts(priv); | 4746 | /* only Re-enable if diabled by irq */ |
4747 | if (test_bit(STATUS_INT_ENABLED, &priv->status)) | ||
4748 | iwl4965_enable_interrupts(priv); | ||
4737 | spin_unlock(&priv->lock); | 4749 | spin_unlock(&priv->lock); |
4738 | return IRQ_NONE; | 4750 | return IRQ_NONE; |
4739 | } | 4751 | } |
@@ -5772,7 +5784,10 @@ static void __iwl4965_down(struct iwl_priv *priv) | |||
5772 | iwl_write32(priv, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); | 5784 | iwl_write32(priv, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); |
5773 | 5785 | ||
5774 | /* tell the device to stop sending interrupts */ | 5786 | /* tell the device to stop sending interrupts */ |
5787 | spin_lock_irqsave(&priv->lock, flags); | ||
5775 | iwl4965_disable_interrupts(priv); | 5788 | iwl4965_disable_interrupts(priv); |
5789 | spin_unlock_irqrestore(&priv->lock, flags); | ||
5790 | iwl_synchronize_irq(priv); | ||
5776 | 5791 | ||
5777 | if (priv->mac80211_registered) | 5792 | if (priv->mac80211_registered) |
5778 | ieee80211_stop_queues(priv->hw); | 5793 | ieee80211_stop_queues(priv->hw); |
@@ -7996,6 +8011,7 @@ static int iwl4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e | |||
7996 | struct iwl_priv *priv; | 8011 | struct iwl_priv *priv; |
7997 | struct ieee80211_hw *hw; | 8012 | struct ieee80211_hw *hw; |
7998 | struct iwl_cfg *cfg = (struct iwl_cfg *)(ent->driver_data); | 8013 | struct iwl_cfg *cfg = (struct iwl_cfg *)(ent->driver_data); |
8014 | unsigned long flags; | ||
7999 | DECLARE_MAC_BUF(mac); | 8015 | DECLARE_MAC_BUF(mac); |
8000 | 8016 | ||
8001 | /************************ | 8017 | /************************ |
@@ -8133,7 +8149,9 @@ static int iwl4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e | |||
8133 | /******************** | 8149 | /******************** |
8134 | * 8. Setup services | 8150 | * 8. Setup services |
8135 | ********************/ | 8151 | ********************/ |
8152 | spin_lock_irqsave(&priv->lock, flags); | ||
8136 | iwl4965_disable_interrupts(priv); | 8153 | iwl4965_disable_interrupts(priv); |
8154 | spin_unlock_irqrestore(&priv->lock, flags); | ||
8137 | 8155 | ||
8138 | err = sysfs_create_group(&pdev->dev.kobj, &iwl4965_attribute_group); | 8156 | err = sysfs_create_group(&pdev->dev.kobj, &iwl4965_attribute_group); |
8139 | if (err) { | 8157 | if (err) { |
@@ -8182,6 +8200,7 @@ static void __devexit iwl4965_pci_remove(struct pci_dev *pdev) | |||
8182 | struct iwl_priv *priv = pci_get_drvdata(pdev); | 8200 | struct iwl_priv *priv = pci_get_drvdata(pdev); |
8183 | struct list_head *p, *q; | 8201 | struct list_head *p, *q; |
8184 | int i; | 8202 | int i; |
8203 | unsigned long flags; | ||
8185 | 8204 | ||
8186 | if (!priv) | 8205 | if (!priv) |
8187 | return; | 8206 | return; |
@@ -8192,6 +8211,15 @@ static void __devexit iwl4965_pci_remove(struct pci_dev *pdev) | |||
8192 | 8211 | ||
8193 | iwl4965_down(priv); | 8212 | iwl4965_down(priv); |
8194 | 8213 | ||
8214 | /* make sure we flush any pending irq or | ||
8215 | * tasklet for the driver | ||
8216 | */ | ||
8217 | spin_lock_irqsave(&priv->lock, flags); | ||
8218 | iwl4965_disable_interrupts(priv); | ||
8219 | spin_unlock_irqrestore(&priv->lock, flags); | ||
8220 | |||
8221 | iwl_synchronize_irq(priv); | ||
8222 | |||
8195 | /* Free MAC hash list for ADHOC */ | 8223 | /* Free MAC hash list for ADHOC */ |
8196 | for (i = 0; i < IWL_IBSS_MAC_HASH_SIZE; i++) { | 8224 | for (i = 0; i < IWL_IBSS_MAC_HASH_SIZE; i++) { |
8197 | list_for_each_safe(p, q, &priv->ibss_mac_hash[i]) { | 8225 | list_for_each_safe(p, q, &priv->ibss_mac_hash[i]) { |