From 9f333281a7da4c3a59bccc0cb53f7590eb850d93 Mon Sep 17 00:00:00 2001 From: Johannes Stezenbach Date: Tue, 30 Nov 2010 16:49:23 +0100 Subject: [PATCH 1/6] mac80211/rt2x00: add ieee80211_tx_status_ni() All rt2x00 drivers except rt2800pci call ieee80211_tx_status() from a workqueue, which causes "NOHZ: local_softirq_pending 08" messages. To fix it, add ieee80211_tx_status_ni() similar to ieee80211_rx_ni() which can be called from process context, and call it from rt2x00lib_txdone(). For the rt2800pci special case a driver flag is introduced. https://bugzilla.kernel.org/show_bug.cgi?id=24892 Signed-off-by: Johannes Stezenbach Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800pci.c | 1 + drivers/net/wireless/rt2x00/rt2x00.h | 1 + drivers/net/wireless/rt2x00/rt2x00dev.c | 9 +++++--- include/net/mac80211.h | 28 +++++++++++++++++++++---- 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c index b26739535986..09a67905c230 100644 --- a/drivers/net/wireless/rt2x00/rt2800pci.c +++ b/drivers/net/wireless/rt2x00/rt2800pci.c @@ -912,6 +912,7 @@ static int rt2800pci_probe_hw(struct rt2x00_dev *rt2x00dev) __set_bit(DRIVER_REQUIRE_DMA, &rt2x00dev->flags); __set_bit(DRIVER_REQUIRE_L2PAD, &rt2x00dev->flags); __set_bit(DRIVER_REQUIRE_TXSTATUS_FIFO, &rt2x00dev->flags); + __set_bit(DRIVER_REQUIRE_TASKLET_CONTEXT, &rt2x00dev->flags); if (!modparam_nohwcrypt) __set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags); __set_bit(DRIVER_SUPPORT_LINK_TUNING, &rt2x00dev->flags); diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 94fe589acfaa..ab43e7ca2a23 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -664,6 +664,7 @@ enum rt2x00_flags { DRIVER_REQUIRE_COPY_IV, DRIVER_REQUIRE_L2PAD, DRIVER_REQUIRE_TXSTATUS_FIFO, + DRIVER_REQUIRE_TASKLET_CONTEXT, /* * Driver features diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 5ba79b935f09..d019830ca840 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -390,9 +390,12 @@ void rt2x00lib_txdone(struct queue_entry *entry, * through a mac80211 library call (RTS/CTS) then we should not * send the status report back. */ - if (!(skbdesc_flags & SKBDESC_NOT_MAC80211)) - ieee80211_tx_status(rt2x00dev->hw, entry->skb); - else + if (!(skbdesc_flags & SKBDESC_NOT_MAC80211)) { + if (test_bit(DRIVER_REQUIRE_TASKLET_CONTEXT, &rt2x00dev->flags)) + ieee80211_tx_status(rt2x00dev->hw, entry->skb); + else + ieee80211_tx_status_ni(rt2x00dev->hw, entry->skb); + } else dev_kfree_skb_any(entry->skb); /* diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 9fdf982d1286..365359b24177 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2024,8 +2024,8 @@ static inline void ieee80211_rx_ni(struct ieee80211_hw *hw, * * This function may not be called in IRQ context. Calls to this function * for a single hardware must be synchronized against each other. Calls - * to this function and ieee80211_tx_status_irqsafe() may not be mixed - * for a single hardware. + * to this function, ieee80211_tx_status_ni() and ieee80211_tx_status_irqsafe() + * may not be mixed for a single hardware. * * @hw: the hardware the frame was transmitted by * @skb: the frame that was transmitted, owned by mac80211 after this call @@ -2033,14 +2033,34 @@ static inline void ieee80211_rx_ni(struct ieee80211_hw *hw, void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb); +/** + * ieee80211_tx_status_ni - transmit status callback (in process context) + * + * Like ieee80211_tx_status() but can be called in process context. + * + * Calls to this function, ieee80211_tx_status() and + * ieee80211_tx_status_irqsafe() may not be mixed + * for a single hardware. + * + * @hw: the hardware the frame was transmitted by + * @skb: the frame that was transmitted, owned by mac80211 after this call + */ +static inline void ieee80211_tx_status_ni(struct ieee80211_hw *hw, + struct sk_buff *skb) +{ + local_bh_disable(); + ieee80211_tx_status(hw, skb); + local_bh_enable(); +} + /** * ieee80211_tx_status_irqsafe - IRQ-safe transmit status callback * * Like ieee80211_tx_status() but can be called in IRQ context * (internally defers to a tasklet.) * - * Calls to this function and ieee80211_tx_status() may not be mixed for a - * single hardware. + * Calls to this function, ieee80211_tx_status() and + * ieee80211_tx_status_ni() may not be mixed for a single hardware. * * @hw: the hardware the frame was transmitted by * @skb: the frame that was transmitted, owned by mac80211 after this call From b2b7ab22fa2e619c20917e825c0da7212ca2efc9 Mon Sep 17 00:00:00 2001 From: Meelis Roos Date: Fri, 17 Dec 2010 23:27:50 +0200 Subject: [PATCH 2/6] hostap: remove netif_stop_queue from init Fix runtime warning with backtrace from hostap by removing netif_stop_queue() call before register_netdev. Tested to work fine on hostap_pci Prism 2.5. (This removes a warning about calling netif_stop_queue before register_netdev is called. -- JWL) Signed-off-by: Meelis Roos Acked-by: David S. Miller Signed-off-by: John W. Linville --- drivers/net/wireless/hostap/hostap_main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/hostap/hostap_main.c b/drivers/net/wireless/hostap/hostap_main.c index 25a2722c8a98..1d9aed645723 100644 --- a/drivers/net/wireless/hostap/hostap_main.c +++ b/drivers/net/wireless/hostap/hostap_main.c @@ -891,7 +891,6 @@ void hostap_setup_dev(struct net_device *dev, local_info_t *local, SET_ETHTOOL_OPS(dev, &prism2_ethtool_ops); - netif_stop_queue(dev); } static int hostap_enable_hostapd(local_info_t *local, int rtnl_locked) From b51aff057c9d0ef6c529dc25fd9f775faf7b6c63 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 22 Dec 2010 10:15:07 +0100 Subject: [PATCH 3/6] mac80211: fix mesh forwarding Under memory pressure, the mac80211 mesh code may helpfully print a message that it failed to clone a mesh frame and then will proceed to crash trying to use it anyway. Fix that. Cc: stable@kernel.org [2.6.27+] Signed-off-by: Johannes Berg Acked-by: Javier Cardona Signed-off-by: John W. Linville --- net/mac80211/rx.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 54fb4a0e76f0..b01e467b76c6 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1788,9 +1788,11 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) fwd_skb = skb_copy(skb, GFP_ATOMIC); - if (!fwd_skb && net_ratelimit()) + if (!fwd_skb && net_ratelimit()) { printk(KERN_DEBUG "%s: failed to clone mesh frame\n", sdata->name); + goto out; + } fwd_hdr = (struct ieee80211_hdr *) fwd_skb->data; memcpy(fwd_hdr->addr2, sdata->vif.addr, ETH_ALEN); @@ -1828,6 +1830,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) } } + out: if (is_multicast_ether_addr(hdr->addr1) || sdata->dev->flags & IFF_PROMISC) return RX_CONTINUE; From 554d1d027b19265c4aa3f718b3126d2b86e09a08 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Thu, 23 Dec 2010 12:38:21 +0100 Subject: [PATCH 4/6] iwlagn: enable only rfkill interrupt when device is down Since commit 6cd0b1cb872b3bf9fc5de4536404206ab74bafdd "iwlagn: fix hw-rfkill while the interface is down", we enable interrupts when device is not ready to receive them. However hardware, when it is in some inconsistent state, can generate other than rfkill interrupts and crash the system. I can reproduce crash with "kernel BUG at drivers/net/wireless/iwlwifi/iwl-agn.c:1010!" message, when forcing firmware restarts. To fix only enable rfkill interrupt when down device and after probe. I checked patch on laptop with 5100 device, rfkill change is still passed to user space when device is down. Signed-off-by: Stanislaw Gruszka Cc: stable@kernel.org Acked-by: Wey-Yi Guy Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-agn.c | 9 +++++---- drivers/net/wireless/iwlwifi/iwl-helpers.h | 6 ++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index c2636a7ab9ee..e0ec40ed43b7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -3530,9 +3530,10 @@ static void iwl_mac_stop(struct ieee80211_hw *hw) flush_workqueue(priv->workqueue); - /* enable interrupts again in order to receive rfkill changes */ + /* User space software may expect getting rfkill changes + * even if interface is down */ iwl_write32(priv, CSR_INT, 0xFFFFFFFF); - iwl_enable_interrupts(priv); + iwl_enable_rfkill_int(priv); IWL_DEBUG_MAC80211(priv, "leave\n"); } @@ -4515,14 +4516,14 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) * 8. Enable interrupts and read RFKILL state *********************************************/ - /* enable interrupts if needed: hw bug w/a */ + /* enable rfkill interrupt: hw bug w/a */ pci_read_config_word(priv->pci_dev, PCI_COMMAND, &pci_cmd); if (pci_cmd & PCI_COMMAND_INTX_DISABLE) { pci_cmd &= ~PCI_COMMAND_INTX_DISABLE; pci_write_config_word(priv->pci_dev, PCI_COMMAND, pci_cmd); } - iwl_enable_interrupts(priv); + iwl_enable_rfkill_int(priv); /* If platform's RF_KILL switch is NOT set to KILL */ if (iwl_read32(priv, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW) diff --git a/drivers/net/wireless/iwlwifi/iwl-helpers.h b/drivers/net/wireless/iwlwifi/iwl-helpers.h index 1aaef70deaec..19f5586d362a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-helpers.h +++ b/drivers/net/wireless/iwlwifi/iwl-helpers.h @@ -163,6 +163,12 @@ static inline void iwl_disable_interrupts(struct iwl_priv *priv) IWL_DEBUG_ISR(priv, "Disabled interrupts\n"); } +static inline void iwl_enable_rfkill_int(struct iwl_priv *priv) +{ + IWL_DEBUG_ISR(priv, "Enabling rfkill interrupt\n"); + iwl_write32(priv, CSR_INT_MASK, CSR_INT_BIT_RF_KILL); +} + static inline void iwl_enable_interrupts(struct iwl_priv *priv) { IWL_DEBUG_ISR(priv, "Enabling interrupts\n"); From 919bbad580445801c22ef6ccbe624551fee652bd Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Thu, 30 Dec 2010 02:01:03 -0600 Subject: [PATCH 5/6] mac80211: fix mesh forwarding when ratelimited too Commit b51aff057c9d0ef6c529dc25fd9f775faf7b6c63 said: Under memory pressure, the mac80211 mesh code may helpfully print a message that it failed to clone a mesh frame and then will proceed to crash trying to use it anyway. Fix that. Avoid the reference whenever the frame copy is unsuccessful regardless of the debug message being suppressed or printed. Cc: stable@kernel.org [2.6.27+] Signed-off-by: Milton Miller Signed-off-by: John W. Linville --- net/mac80211/rx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index b01e467b76c6..e98668fab503 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1788,11 +1788,11 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) fwd_skb = skb_copy(skb, GFP_ATOMIC); - if (!fwd_skb && net_ratelimit()) { + if (!fwd_skb && net_ratelimit()) printk(KERN_DEBUG "%s: failed to clone mesh frame\n", sdata->name); + if (!fwd_skb) goto out; - } fwd_hdr = (struct ieee80211_hdr *) fwd_skb->data; memcpy(fwd_hdr->addr2, sdata->vif.addr, ETH_ALEN); From d2460f4b2fa6dbdeec800414f9cf5b1fc8b71197 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 3 Jan 2011 19:42:24 +0100 Subject: [PATCH 6/6] mac80211: add missing synchronize_rcu commit ad0e2b5a00dbec303e4682b403bb6703d11dcdb2 Author: Johannes Berg Date: Tue Jun 1 10:19:19 2010 +0200 mac80211: simplify key locking removed the synchronization against RCU and thus opened a race window where we can use a key for TX while it is already freed. Put a synchronisation into the right place to close that window. Reported-by: Jussi Kivilinna Cc: stable@kernel.org [2.6.36+] Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/key.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/mac80211/key.c b/net/mac80211/key.c index ccd676b2f599..aa1b734a5e99 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -366,6 +366,12 @@ static void __ieee80211_key_destroy(struct ieee80211_key *key) if (!key) return; + /* + * Synchronize so the TX path can no longer be using + * this key before we free/remove it. + */ + synchronize_rcu(); + if (key->local) ieee80211_key_disable_hw_accel(key);