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 | |
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>
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl3945-base.c | 34 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl4965-base.c | 32 |
2 files changed, 62 insertions, 4 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]) { |
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]) { |