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/iwl3945-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/iwl3945-base.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl3945-base.c | 34 |
1 files changed, 32 insertions, 2 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index ab635dcfd5d1..2a5245b63e32 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c | |||
@@ -4153,6 +4153,16 @@ static void iwl3945_enable_interrupts(struct iwl3945_priv *priv) | |||
4153 | iwl3945_write32(priv, CSR_INT_MASK, CSR_INI_SET_MASK); | 4153 | iwl3945_write32(priv, CSR_INT_MASK, CSR_INI_SET_MASK); |
4154 | } | 4154 | } |
4155 | 4155 | ||
4156 | |||
4157 | /* call this function to flush any scheduled tasklet */ | ||
4158 | static inline void iwl_synchronize_irq(struct iwl3945_priv *priv) | ||
4159 | { | ||
4160 | /* wait to make sure we flush pedding tasklet*/ | ||
4161 | synchronize_irq(priv->pci_dev->irq); | ||
4162 | tasklet_kill(&priv->irq_tasklet); | ||
4163 | } | ||
4164 | |||
4165 | |||
4156 | static inline void iwl3945_disable_interrupts(struct iwl3945_priv *priv) | 4166 | static inline void iwl3945_disable_interrupts(struct iwl3945_priv *priv) |
4157 | { | 4167 | { |
4158 | clear_bit(STATUS_INT_ENABLED, &priv->status); | 4168 | clear_bit(STATUS_INT_ENABLED, &priv->status); |
@@ -4552,7 +4562,9 @@ static void iwl3945_irq_tasklet(struct iwl3945_priv *priv) | |||
4552 | } | 4562 | } |
4553 | 4563 | ||
4554 | /* Re-enable all interrupts */ | 4564 | /* Re-enable all interrupts */ |
4555 | iwl3945_enable_interrupts(priv); | 4565 | /* only Re-enable if disabled by irq */ |
4566 | if (test_bit(STATUS_INT_ENABLED, &priv->status)) | ||
4567 | iwl3945_enable_interrupts(priv); | ||
4556 | 4568 | ||
4557 | #ifdef CONFIG_IWL3945_DEBUG | 4569 | #ifdef CONFIG_IWL3945_DEBUG |
4558 | if (iwl3945_debug_level & (IWL_DL_ISR)) { | 4570 | if (iwl3945_debug_level & (IWL_DL_ISR)) { |
@@ -4616,7 +4628,9 @@ unplugged: | |||
4616 | 4628 | ||
4617 | none: | 4629 | none: |
4618 | /* re-enable interrupts here since we don't have anything to service. */ | 4630 | /* re-enable interrupts here since we don't have anything to service. */ |
4619 | iwl3945_enable_interrupts(priv); | 4631 | /* only Re-enable if disabled by irq */ |
4632 | if (test_bit(STATUS_INT_ENABLED, &priv->status)) | ||
4633 | iwl3945_enable_interrupts(priv); | ||
4620 | spin_unlock(&priv->lock); | 4634 | spin_unlock(&priv->lock); |
4621 | return IRQ_NONE; | 4635 | return IRQ_NONE; |
4622 | } | 4636 | } |
@@ -5905,7 +5919,10 @@ static void __iwl3945_down(struct iwl3945_priv *priv) | |||
5905 | iwl3945_write32(priv, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); | 5919 | iwl3945_write32(priv, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); |
5906 | 5920 | ||
5907 | /* tell the device to stop sending interrupts */ | 5921 | /* tell the device to stop sending interrupts */ |
5922 | spin_lock_irqsave(&priv->lock, flags); | ||
5908 | iwl3945_disable_interrupts(priv); | 5923 | iwl3945_disable_interrupts(priv); |
5924 | spin_unlock_irqrestore(&priv->lock, flags); | ||
5925 | iwl_synchronize_irq(priv); | ||
5909 | 5926 | ||
5910 | if (priv->mac80211_registered) | 5927 | if (priv->mac80211_registered) |
5911 | ieee80211_stop_queues(priv->hw); | 5928 | ieee80211_stop_queues(priv->hw); |
@@ -7943,6 +7960,7 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e | |||
7943 | struct ieee80211_hw *hw; | 7960 | struct ieee80211_hw *hw; |
7944 | struct iwl_3945_cfg *cfg = (struct iwl_3945_cfg *)(ent->driver_data); | 7961 | struct iwl_3945_cfg *cfg = (struct iwl_3945_cfg *)(ent->driver_data); |
7945 | int i; | 7962 | int i; |
7963 | unsigned long flags; | ||
7946 | DECLARE_MAC_BUF(mac); | 7964 | DECLARE_MAC_BUF(mac); |
7947 | 7965 | ||
7948 | /* Disabling hardware scan means that mac80211 will perform scans | 7966 | /* Disabling hardware scan means that mac80211 will perform scans |
@@ -8093,7 +8111,9 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e | |||
8093 | priv->power_mode = IWL_POWER_AC; | 8111 | priv->power_mode = IWL_POWER_AC; |
8094 | priv->user_txpower_limit = IWL_DEFAULT_TX_POWER; | 8112 | priv->user_txpower_limit = IWL_DEFAULT_TX_POWER; |
8095 | 8113 | ||
8114 | spin_lock_irqsave(&priv->lock, flags); | ||
8096 | iwl3945_disable_interrupts(priv); | 8115 | iwl3945_disable_interrupts(priv); |
8116 | spin_unlock_irqrestore(&priv->lock, flags); | ||
8097 | 8117 | ||
8098 | err = sysfs_create_group(&pdev->dev.kobj, &iwl3945_attribute_group); | 8118 | err = sysfs_create_group(&pdev->dev.kobj, &iwl3945_attribute_group); |
8099 | if (err) { | 8119 | if (err) { |
@@ -8180,6 +8200,7 @@ static void __devexit iwl3945_pci_remove(struct pci_dev *pdev) | |||
8180 | struct iwl3945_priv *priv = pci_get_drvdata(pdev); | 8200 | struct iwl3945_priv *priv = pci_get_drvdata(pdev); |
8181 | struct list_head *p, *q; | 8201 | struct list_head *p, *q; |
8182 | int i; | 8202 | int i; |
8203 | unsigned long flags; | ||
8183 | 8204 | ||
8184 | if (!priv) | 8205 | if (!priv) |
8185 | return; | 8206 | return; |
@@ -8190,6 +8211,15 @@ static void __devexit iwl3945_pci_remove(struct pci_dev *pdev) | |||
8190 | 8211 | ||
8191 | iwl3945_down(priv); | 8212 | iwl3945_down(priv); |
8192 | 8213 | ||
8214 | /* make sure we flush any pending irq or | ||
8215 | * tasklet for the driver | ||
8216 | */ | ||
8217 | spin_lock_irqsave(&priv->lock, flags); | ||
8218 | iwl3945_disable_interrupts(priv); | ||
8219 | spin_unlock_irqrestore(&priv->lock, flags); | ||
8220 | |||
8221 | iwl_synchronize_irq(priv); | ||
8222 | |||
8193 | /* Free MAC hash list for ADHOC */ | 8223 | /* Free MAC hash list for ADHOC */ |
8194 | for (i = 0; i < IWL_IBSS_MAC_HASH_SIZE; i++) { | 8224 | for (i = 0; i < IWL_IBSS_MAC_HASH_SIZE; i++) { |
8195 | list_for_each_safe(p, q, &priv->ibss_mac_hash[i]) { | 8225 | list_for_each_safe(p, q, &priv->ibss_mac_hash[i]) { |