From a8b875e7dc80ff442698d8cf4f45ccce400a6a66 Mon Sep 17 00:00:00 2001 From: Jussi Kivilinna Date: Thu, 13 Aug 2009 20:39:31 +0300 Subject: cfg80211: export cfg80211_wext_siwfreq cfg80211_wext_siwfreq() should be exported with EXPORT_SYMBOL_GPL. Signed-off-by: Jussi Kivilinna Acked-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/wext-compat.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index c44917492210..c12029b1def0 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -771,6 +771,7 @@ int cfg80211_wext_siwfreq(struct net_device *dev, return err; } } +EXPORT_SYMBOL_GPL(cfg80211_wext_siwfreq); int cfg80211_wext_giwfreq(struct net_device *dev, struct iw_request_info *info, -- cgit v1.2.3-58-ga151 From 0ff6ce7b36199f67f709c750e9a2a66659a4babe Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 17 Aug 2009 12:25:37 +0200 Subject: cfg80211: fix deadlock When removing an interface with nl80211, cfg80211 will deadlock in the netdev notifier because we're already holding rdev->mtx and try to acquire it again to verify the scan has been done. This bug was introduced by my patch "cfg80211: check for and abort dangling scan requests". To fix this, move the dangling scan request check into wiphy_unregister(). This will not be able to catch all cases right away, but if the scan problem happens with a manual ifdown or so it will be possible to remedy it by removing the module/device. Additionally, add comments about the deadlock scenario. Reported-by: Christian Lamparter Signed-off-by: Johannes Berg Tested-by: Christian Lamparter Tested-by: Kalle Valo Signed-off-by: John W. Linville --- net/wireless/core.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) (limited to 'net') diff --git a/net/wireless/core.c b/net/wireless/core.c index bc99e4ec7463..69a185ba9ff1 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -586,9 +586,14 @@ void wiphy_unregister(struct wiphy *wiphy) * get to lock contention here if userspace issues a command * that identified the hardware by wiphy index. */ - mutex_lock(&rdev->mtx); - /* unlock again before freeing */ - mutex_unlock(&rdev->mtx); + cfg80211_lock_rdev(rdev); + + if (WARN_ON(rdev->scan_req)) { + rdev->scan_req->aborted = true; + ___cfg80211_scan_done(rdev); + } + + cfg80211_unlock_rdev(rdev); cfg80211_debugfs_rdev_del(rdev); @@ -605,7 +610,6 @@ void wiphy_unregister(struct wiphy *wiphy) flush_work(&rdev->scan_done_wk); cancel_work_sync(&rdev->conn_work); - kfree(rdev->scan_req); flush_work(&rdev->event_work); } EXPORT_SYMBOL(wiphy_unregister); @@ -653,6 +657,11 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, switch (state) { case NETDEV_REGISTER: + /* + * NB: cannot take rdev->mtx here because this may be + * called within code protected by it when interfaces + * are added with nl80211. + */ mutex_init(&wdev->mtx); INIT_LIST_HEAD(&wdev->event_list); spin_lock_init(&wdev->event_lock); @@ -730,13 +739,11 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, #endif break; case NETDEV_UNREGISTER: - cfg80211_lock_rdev(rdev); - - if (WARN_ON(rdev->scan_req && rdev->scan_req->dev == dev)) { - rdev->scan_req->aborted = true; - ___cfg80211_scan_done(rdev); - } - + /* + * NB: cannot take rdev->mtx here because this may be + * called within code protected by it when interfaces + * are removed with nl80211. + */ mutex_lock(&rdev->devlist_mtx); /* * It is possible to get NETDEV_UNREGISTER @@ -755,7 +762,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, #endif } mutex_unlock(&rdev->devlist_mtx); - cfg80211_unlock_rdev(rdev); break; case NETDEV_PRE_UP: if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype))) -- cgit v1.2.3-58-ga151 From ea416a793d2b611f22b42ba094fd2e5bd30fff43 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 17 Aug 2009 12:22:14 +0200 Subject: cfg80211: report userspace SME connected event properly Instead of hacking the event reporting into the __cfg80211_connect_result() function which is also invoked by others, set up things correctly and then invoke that function, so that it can do more sanity checking. Also, it is currently not possible to get a ROAMED event from the userspace SME anyway since we send out a DISCONNECTED event when it disassociates and then a new CONNECTED event on the next association. Thanks to Zhu Yi for pointing out that the code is somewhat convoluted and doesn't warn when it should. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/mlme.c | 9 +++++++++ net/wireless/sme.c | 22 ++++++---------------- 2 files changed, 15 insertions(+), 16 deletions(-) (limited to 'net') diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index da64071ceb84..79d2eec54cec 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -96,6 +96,15 @@ void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len) WARN_ON(!bss); } + if (!wdev->conn && wdev->sme_state == CFG80211_SME_IDLE) { + /* + * This is for the userspace SME, the CONNECTING + * state will be changed to CONNECTED by + * __cfg80211_connect_result() below. + */ + wdev->sme_state = CFG80211_SME_CONNECTING; + } + /* this consumes one bss reference (unless bss is NULL) */ __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, status_code, diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 8e2ef54ea714..6fb6a704ca40 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -351,15 +351,13 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) return; - if (wdev->sme_state == CFG80211_SME_CONNECTED) - nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), dev, + if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING)) + return; + + nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, bssid, req_ie, req_ie_len, - resp_ie, resp_ie_len, GFP_KERNEL); - else - nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, - bssid, req_ie, req_ie_len, - resp_ie, resp_ie_len, - status, GFP_KERNEL); + resp_ie, resp_ie_len, + status, GFP_KERNEL); #ifdef CONFIG_WIRELESS_EXT if (wextev) { @@ -392,13 +390,6 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, wdev->current_bss = NULL; } - if (status == WLAN_STATUS_SUCCESS && - wdev->sme_state == CFG80211_SME_IDLE) - goto success; - - if (wdev->sme_state != CFG80211_SME_CONNECTING) - return; - if (wdev->conn) wdev->conn->state = CFG80211_CONN_IDLE; @@ -412,7 +403,6 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, return; } - success: if (!bss) bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, wdev->ssid, wdev->ssid_len, -- cgit v1.2.3-58-ga151 From 3ac64beecd27400d12cc7afb4108eef26c499f6a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 17 Aug 2009 16:16:53 +0200 Subject: mac80211: allow configure_filter callback to sleep Over time, a whole bunch of drivers have come up with their own scheme to delay the configure_filter operation to a workqueue. To be able to simplify things, allow configure_filter to sleep, and add a new prepare_multicast callback that drivers that need the multicast address list implement. This new callback must be atomic, but most drivers either don't care or just calculate a hash which can be done atomically and then uploaded to the hardware non-atomically. A cursory look suggests that at76c50x-usb, ar9170, mwl8k (which is actually very broken now), rt2x00, wl1251, wl1271 and zd1211 should make use of this new capability. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/adm8211.c | 42 +++++++++++++------- drivers/net/wireless/at76c50x-usb.c | 7 ++-- drivers/net/wireless/ath/ar9170/main.c | 43 +++++++++++--------- drivers/net/wireless/ath/ath5k/base.c | 64 ++++++++++++++++++------------ drivers/net/wireless/ath/ath9k/main.c | 3 +- drivers/net/wireless/b43/main.c | 2 +- drivers/net/wireless/b43legacy/main.c | 4 +- drivers/net/wireless/iwlwifi/iwl-core.c | 2 +- drivers/net/wireless/iwlwifi/iwl-core.h | 3 +- drivers/net/wireless/libertas_tf/main.c | 37 +++++++++++------ drivers/net/wireless/mac80211_hwsim.c | 4 +- drivers/net/wireless/mwl8k.c | 34 ++++++++++++---- drivers/net/wireless/p54/main.c | 2 +- drivers/net/wireless/rt2x00/rt2x00.h | 2 +- drivers/net/wireless/rt2x00/rt2x00mac.c | 2 +- drivers/net/wireless/rtl818x/rtl8180_dev.c | 11 ++++- drivers/net/wireless/rtl818x/rtl8187_dev.c | 11 ++++- drivers/net/wireless/wl12xx/wl1251_main.c | 4 +- drivers/net/wireless/wl12xx/wl1271_main.c | 4 +- drivers/net/wireless/zd1211rw/zd_mac.c | 44 +++++++++++++------- include/net/mac80211.h | 21 +++++++--- net/mac80211/driver-ops.h | 24 +++++++++-- net/mac80211/driver-trace.h | 36 ++++++++++++++--- net/mac80211/ieee80211_i.h | 3 ++ net/mac80211/iface.c | 15 ++----- net/mac80211/main.c | 24 ++++++++--- net/mac80211/scan.c | 16 +------- net/mac80211/util.c | 2 - 28 files changed, 297 insertions(+), 169 deletions(-) (limited to 'net') diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c index 5695911bc602..b80f514877d8 100644 --- a/drivers/net/wireless/adm8211.c +++ b/drivers/net/wireless/adm8211.c @@ -1328,16 +1328,39 @@ static void adm8211_bss_info_changed(struct ieee80211_hw *dev, } } +static u64 adm8211_prepare_multicast(struct ieee80211_hw *hw, + int mc_count, struct dev_addr_list *mclist) +{ + unsigned int bit_nr, i; + u32 mc_filter[2]; + + mc_filter[1] = mc_filter[0] = 0; + + for (i = 0; i < mc_count; i++) { + if (!mclist) + break; + bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26; + + bit_nr &= 0x3F; + mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); + mclist = mclist->next; + } + + return mc_filter[0] | ((u64)(mc_filter[1]) << 32); +} + static void adm8211_configure_filter(struct ieee80211_hw *dev, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, struct dev_mc_list *mclist) + u64 multicast) { static const u8 bcast[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; struct adm8211_priv *priv = dev->priv; - unsigned int bit_nr, new_flags; + unsigned int new_flags; u32 mc_filter[2]; - int i; + + mc_filter[0] = multicast; + mc_filter[1] = multicast >> 32; new_flags = 0; @@ -1346,23 +1369,13 @@ static void adm8211_configure_filter(struct ieee80211_hw *dev, priv->nar |= ADM8211_NAR_PR; priv->nar &= ~ADM8211_NAR_MM; mc_filter[1] = mc_filter[0] = ~0; - } else if ((*total_flags & FIF_ALLMULTI) || (mc_count > 32)) { + } else if (*total_flags & FIF_ALLMULTI || multicast == ~(0ULL)) { new_flags |= FIF_ALLMULTI; priv->nar &= ~ADM8211_NAR_PR; priv->nar |= ADM8211_NAR_MM; mc_filter[1] = mc_filter[0] = ~0; } else { priv->nar &= ~(ADM8211_NAR_MM | ADM8211_NAR_PR); - mc_filter[1] = mc_filter[0] = 0; - for (i = 0; i < mc_count; i++) { - if (!mclist) - break; - bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26; - - bit_nr &= 0x3F; - mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); - mclist = mclist->next; - } } ADM8211_IDLE_RX(); @@ -1757,6 +1770,7 @@ static const struct ieee80211_ops adm8211_ops = { .remove_interface = adm8211_remove_interface, .config = adm8211_config, .bss_info_changed = adm8211_bss_info_changed, + .prepare_multicast = adm8211_prepare_multicast, .configure_filter = adm8211_configure_filter, .get_stats = adm8211_get_stats, .get_tx_stats = adm8211_get_tx_stats, diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c index 7218dbabad3e..a6e19545ac6a 100644 --- a/drivers/net/wireless/at76c50x-usb.c +++ b/drivers/net/wireless/at76c50x-usb.c @@ -1997,15 +1997,14 @@ static void at76_bss_info_changed(struct ieee80211_hw *hw, /* must be atomic */ static void at76_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, - unsigned int *total_flags, int mc_count, - struct dev_addr_list *mc_list) + unsigned int *total_flags, u64 multicast) { struct at76_priv *priv = hw->priv; int flags; at76_dbg(DBG_MAC80211, "%s(): changed_flags=0x%08x " - "total_flags=0x%08x mc_count=%d", - __func__, changed_flags, *total_flags, mc_count); + "total_flags=0x%08x", + __func__, changed_flags, *total_flags); flags = changed_flags & AT76_SUPPORTED_FILTERS; *total_flags = AT76_SUPPORTED_FILTERS; diff --git a/drivers/net/wireless/ath/ar9170/main.c b/drivers/net/wireless/ath/ar9170/main.c index ea8c9419336d..6a9462e4fd87 100644 --- a/drivers/net/wireless/ath/ar9170/main.c +++ b/drivers/net/wireless/ath/ar9170/main.c @@ -2100,10 +2100,29 @@ unlock: mutex_unlock(&ar->mutex); } +static u64 ar9170_op_prepare_multicast(struct ieee80211_hw *hw, int mc_count, + struct dev_addr_list *mclist) +{ + u64 mchash; + int i; + + /* always get broadcast frames */ + mchash = 1ULL << (0xff >> 2); + + for (i = 0; i < mc_count; i++) { + if (WARN_ON(!mclist)) + break; + mchash |= 1ULL << (mclist->dmi_addr[5] >> 2); + mclist = mclist->next; + } + + return mchash; +} + static void ar9170_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *new_flags, - int mc_count, struct dev_mc_list *mclist) + u64 multicast) { struct ar9170 *ar = hw->priv; @@ -2116,24 +2135,11 @@ static void ar9170_op_configure_filter(struct ieee80211_hw *hw, * then checking the error flags, later. */ - if (changed_flags & FIF_ALLMULTI) { - if (*new_flags & FIF_ALLMULTI) { - ar->want_mc_hash = ~0ULL; - } else { - u64 mchash; - int i; - - /* always get broadcast frames */ - mchash = 1ULL << (0xff >> 2); + if (changed_flags & FIF_ALLMULTI && *new_flags & FIF_ALLMULTI) + multicast = ~0ULL; - for (i = 0; i < mc_count; i++) { - if (WARN_ON(!mclist)) - break; - mchash |= 1ULL << (mclist->dmi_addr[5] >> 2); - mclist = mclist->next; - } - ar->want_mc_hash = mchash; - } + if (multicast != ar->want_mc_hash) { + ar->want_mc_hash = multicast; set_bit(AR9170_FILTER_CHANGED_MULTICAST, &ar->filter_changed); } @@ -2543,6 +2549,7 @@ static const struct ieee80211_ops ar9170_ops = { .add_interface = ar9170_op_add_interface, .remove_interface = ar9170_op_remove_interface, .config = ar9170_op_config, + .prepare_multicast = ar9170_op_prepare_multicast, .configure_filter = ar9170_op_configure_filter, .conf_tx = ar9170_conf_tx, .bss_info_changed = ar9170_op_bss_info_changed, diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 2b3cf39dd4b1..3951b5b13424 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -229,10 +229,12 @@ static int ath5k_add_interface(struct ieee80211_hw *hw, static void ath5k_remove_interface(struct ieee80211_hw *hw, struct ieee80211_if_init_conf *conf); static int ath5k_config(struct ieee80211_hw *hw, u32 changed); +static u64 ath5k_prepare_multicast(struct ieee80211_hw *hw, + int mc_count, struct dev_addr_list *mc_list); static void ath5k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *new_flags, - int mc_count, struct dev_mc_list *mclist); + u64 multicast); static int ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -260,6 +262,7 @@ static const struct ieee80211_ops ath5k_hw_ops = { .add_interface = ath5k_add_interface, .remove_interface = ath5k_remove_interface, .config = ath5k_config, + .prepare_multicast = ath5k_prepare_multicast, .configure_filter = ath5k_configure_filter, .set_key = ath5k_set_key, .get_stats = ath5k_get_stats, @@ -2853,6 +2856,37 @@ unlock: return ret; } +static u64 ath5k_prepare_multicast(struct ieee80211_hw *hw, + int mc_count, struct dev_addr_list *mclist) +{ + u32 mfilt[2], val; + int i; + u8 pos; + + mfilt[0] = 0; + mfilt[1] = 1; + + for (i = 0; i < mc_count; i++) { + if (!mclist) + break; + /* calculate XOR of eight 6-bit values */ + val = get_unaligned_le32(mclist->dmi_addr + 0); + pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; + val = get_unaligned_le32(mclist->dmi_addr + 3); + pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; + pos &= 0x3f; + mfilt[pos / 32] |= (1 << (pos % 32)); + /* XXX: we might be able to just do this instead, + * but not sure, needs testing, if we do use this we'd + * neet to inform below to not reset the mcast */ + /* ath5k_hw_set_mcast_filterindex(ah, + * mclist->dmi_addr[5]); */ + mclist = mclist->next; + } + + return ((u64)(mfilt[1]) << 32) | mfilt[0]; +} + #define SUPPORTED_FIF_FLAGS \ FIF_PROMISC_IN_BSS | FIF_ALLMULTI | FIF_FCSFAIL | \ FIF_PLCPFAIL | FIF_CONTROL | FIF_OTHER_BSS | \ @@ -2878,16 +2912,14 @@ unlock: static void ath5k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *new_flags, - int mc_count, struct dev_mc_list *mclist) + u64 multicast) { struct ath5k_softc *sc = hw->priv; struct ath5k_hw *ah = sc->ah; - u32 mfilt[2], val, rfilt; - u8 pos; - int i; + u32 mfilt[2], rfilt; - mfilt[0] = 0; - mfilt[1] = 0; + mfilt[0] = multicast; + mfilt[1] = multicast >> 32; /* Only deal with supported flags */ changed_flags &= SUPPORTED_FIF_FLAGS; @@ -2913,24 +2945,6 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw, if (*new_flags & FIF_ALLMULTI) { mfilt[0] = ~0; mfilt[1] = ~0; - } else { - for (i = 0; i < mc_count; i++) { - if (!mclist) - break; - /* calculate XOR of eight 6-bit values */ - val = get_unaligned_le32(mclist->dmi_addr + 0); - pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; - val = get_unaligned_le32(mclist->dmi_addr + 3); - pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; - pos &= 0x3f; - mfilt[pos / 32] |= (1 << (pos % 32)); - /* XXX: we might be able to just do this instead, - * but not sure, needs testing, if we do use this we'd - * neet to inform below to not reset the mcast */ - /* ath5k_hw_set_mcast_filterindex(ah, - * mclist->dmi_addr[5]); */ - mclist = mclist->next; - } } /* This is the best we can do */ diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 3e09b9ac165b..2f9c149fd481 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -2394,8 +2394,7 @@ skip_chan_change: static void ath9k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, - struct dev_mc_list *mclist) + u64 multicast) { struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index c5bece090420..78ddbc7f836b 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -3679,7 +3679,7 @@ out_unlock: static void b43_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed, unsigned int *fflags, - int mc_count, struct dev_addr_list *mc_list) + u64 multicast) { struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wldev *dev = wl->current_dev; diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index b1435594921a..b166a6f9f055 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -2836,9 +2836,7 @@ static void b43legacy_op_bss_info_changed(struct ieee80211_hw *hw, static void b43legacy_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed, - unsigned int *fflags, - int mc_count, - struct dev_addr_list *mc_list) + unsigned int *fflags,u64 multicast) { struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); struct b43legacy_wldev *dev = wl->current_dev; diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index c0efa6623c09..f1f6dabd8fbd 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -1514,7 +1514,7 @@ EXPORT_SYMBOL(iwl_irq_handle_error); void iwl_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, struct dev_addr_list *mc_list) + u64 multicast) { struct iwl_priv *priv = hw->priv; __le32 *filter_flags = &priv->staging_rxon.filter_flags; diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h index 4ca025a34daf..62d90364b61d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.h +++ b/drivers/net/wireless/iwlwifi/iwl-core.h @@ -282,8 +282,7 @@ int iwl_set_decrypted_flag(struct iwl_priv *priv, void iwl_irq_handle_error(struct iwl_priv *priv); void iwl_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, - unsigned int *total_flags, - int mc_count, struct dev_addr_list *mc_list); + unsigned int *total_flags, u64 multicast); int iwl_hw_nic_init(struct iwl_priv *priv); int iwl_setup_mac(struct iwl_priv *priv); int iwl_set_hw_params(struct iwl_priv *priv); diff --git a/drivers/net/wireless/libertas_tf/main.c b/drivers/net/wireless/libertas_tf/main.c index 4872345a2f61..019431d2f8a9 100644 --- a/drivers/net/wireless/libertas_tf/main.c +++ b/drivers/net/wireless/libertas_tf/main.c @@ -366,15 +366,35 @@ static int lbtf_op_config(struct ieee80211_hw *hw, u32 changed) return 0; } +static u64 lbtf_op_prepare_multicast(struct ieee80211_hw *hw, + int mc_count, struct dev_addr_list *mclist) +{ + struct lbtf_private *priv = hw->priv; + int i; + + if (!mc_count || mc_count > MRVDRV_MAX_MULTICAST_LIST_SIZE) + return mc_count; + + priv->nr_of_multicastmacaddr = mc_count; + for (i = 0; i < mc_count; i++) { + if (!mclist) + break; + memcpy(&priv->multicastlist[i], mclist->da_addr, + ETH_ALEN); + mclist = mclist->next; + } + + return mc_count; +} + #define SUPPORTED_FIF_FLAGS (FIF_PROMISC_IN_BSS | FIF_ALLMULTI) static void lbtf_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *new_flags, - int mc_count, struct dev_mc_list *mclist) + u64 multicast) { struct lbtf_private *priv = hw->priv; int old_mac_control = priv->mac_control; - int i; changed_flags &= SUPPORTED_FIF_FLAGS; *new_flags &= SUPPORTED_FIF_FLAGS; @@ -386,20 +406,12 @@ static void lbtf_op_configure_filter(struct ieee80211_hw *hw, else priv->mac_control &= ~CMD_ACT_MAC_PROMISCUOUS_ENABLE; if (*new_flags & (FIF_ALLMULTI) || - mc_count > MRVDRV_MAX_MULTICAST_LIST_SIZE) { + multicast > MRVDRV_MAX_MULTICAST_LIST_SIZE) { priv->mac_control |= CMD_ACT_MAC_ALL_MULTICAST_ENABLE; priv->mac_control &= ~CMD_ACT_MAC_MULTICAST_ENABLE; - } else if (mc_count) { + } else if (multicast) { priv->mac_control |= CMD_ACT_MAC_MULTICAST_ENABLE; priv->mac_control &= ~CMD_ACT_MAC_ALL_MULTICAST_ENABLE; - priv->nr_of_multicastmacaddr = mc_count; - for (i = 0; i < mc_count; i++) { - if (!mclist) - break; - memcpy(&priv->multicastlist[i], mclist->da_addr, - ETH_ALEN); - mclist = mclist->next; - } lbtf_cmd_set_mac_multicast_addr(priv); } else { priv->mac_control &= ~(CMD_ACT_MAC_MULTICAST_ENABLE | @@ -461,6 +473,7 @@ static const struct ieee80211_ops lbtf_ops = { .add_interface = lbtf_op_add_interface, .remove_interface = lbtf_op_remove_interface, .config = lbtf_op_config, + .prepare_multicast = lbtf_op_prepare_multicast, .configure_filter = lbtf_op_configure_filter, .bss_info_changed = lbtf_op_bss_info_changed, }; diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 930f5c7da4a6..6f6cd43592c8 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -582,9 +582,7 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed) static void mac80211_hwsim_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, - unsigned int *total_flags, - int mc_count, - struct dev_addr_list *mc_list) + unsigned int *total_flags,u64 multicast) { struct mac80211_hwsim_data *data = hw->priv; diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 8a6d3afe4122..f84387083e73 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -3251,31 +3251,50 @@ mwl8k_configure_filter_exit: return rc; } +static u64 mwl8k_prepare_multicast(struct ieee80211_hw *hw, + int mc_count, struct dev_addr_list *mclist) +{ + struct mwl8k_configure_filter_worker *worker; + + worker = kzalloc(sizeof(*worker), GFP_ATOMIC); + + if (!worker) + return 0; + + /* + * XXX: This is _HORRIBLY_ broken!! + * + * No locking, the mclist pointer might be invalid as soon as this + * function returns, something in the list might be invalidated + * once we get to the worker, etc... + */ + worker->mc_count = mc_count; + worker->mclist = mclist; + + return (u64)worker; +} + static void mwl8k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, - struct dev_addr_list *mclist) + u64 multicast) { - struct mwl8k_configure_filter_worker *worker; + struct mwl8k_configure_filter_worker *worker = (void *)multicast; struct mwl8k_priv *priv = hw->priv; /* Clear unsupported feature flags */ *total_flags &= MWL8K_SUPPORTED_IF_FLAGS; - if (!(changed_flags & MWL8K_SUPPORTED_IF_FLAGS) && !mc_count) + if (!(changed_flags & MWL8K_SUPPORTED_IF_FLAGS)) return; - worker = kzalloc(sizeof(*worker), GFP_ATOMIC); if (worker == NULL) return; worker->header.options = MWL8K_WQ_QUEUE_ONLY | MWL8K_WQ_TX_WAIT_EMPTY; worker->changed_flags = changed_flags; worker->total_flags = total_flags; - worker->mc_count = mc_count; - worker->mclist = mclist; mwl8k_queue_work(hw, &worker->header, priv->config_wq, mwl8k_configure_filter_wt); @@ -3441,6 +3460,7 @@ static const struct ieee80211_ops mwl8k_ops = { .remove_interface = mwl8k_remove_interface, .config = mwl8k_config, .bss_info_changed = mwl8k_bss_info_changed, + .prepare_multicast = mwl8k_prepare_multicast, .configure_filter = mwl8k_configure_filter, .set_rts_threshold = mwl8k_set_rts_threshold, .conf_tx = mwl8k_conf_tx, diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c index 77203e346cda..4d486bf9f725 100644 --- a/drivers/net/wireless/p54/main.c +++ b/drivers/net/wireless/p54/main.c @@ -302,7 +302,7 @@ out: static void p54_configure_filter(struct ieee80211_hw *dev, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, struct dev_mc_list *mclist) + u64 multicast) { struct p54_common *priv = dev->priv; diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 99e89596cef6..39d7d9baafdd 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -978,7 +978,7 @@ int rt2x00mac_config(struct ieee80211_hw *hw, u32 changed); void rt2x00mac_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, struct dev_addr_list *mc_list); + u64 multicast); int rt2x00mac_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set); #ifdef CONFIG_RT2X00_LIB_CRYPTO diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index cb7b6d459331..602f12699718 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -379,7 +379,7 @@ EXPORT_SYMBOL_GPL(rt2x00mac_config); void rt2x00mac_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, struct dev_addr_list *mc_list) + u64 multicast) { struct rt2x00_dev *rt2x00dev = hw->priv; diff --git a/drivers/net/wireless/rtl818x/rtl8180_dev.c b/drivers/net/wireless/rtl818x/rtl8180_dev.c index 09f46abc730a..16429c49139c 100644 --- a/drivers/net/wireless/rtl818x/rtl8180_dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180_dev.c @@ -728,10 +728,16 @@ static void rtl8180_bss_info_changed(struct ieee80211_hw *dev, priv->rf->conf_erp(dev, info); } +static u64 rtl8180_prepare_multicast(struct ieee80211_hw *dev, int mc_count, + struct dev_addr_list *mc_list) +{ + return mc_count; +} + static void rtl8180_configure_filter(struct ieee80211_hw *dev, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, struct dev_addr_list *mclist) + u64 multicast) { struct rtl8180_priv *priv = dev->priv; @@ -741,7 +747,7 @@ static void rtl8180_configure_filter(struct ieee80211_hw *dev, priv->rx_conf ^= RTL818X_RX_CONF_CTRL; if (changed_flags & FIF_OTHER_BSS) priv->rx_conf ^= RTL818X_RX_CONF_MONITOR; - if (*total_flags & FIF_ALLMULTI || mc_count > 0) + if (*total_flags & FIF_ALLMULTI || multicast > 0) priv->rx_conf |= RTL818X_RX_CONF_MULTICAST; else priv->rx_conf &= ~RTL818X_RX_CONF_MULTICAST; @@ -768,6 +774,7 @@ static const struct ieee80211_ops rtl8180_ops = { .remove_interface = rtl8180_remove_interface, .config = rtl8180_config, .bss_info_changed = rtl8180_bss_info_changed, + .prepare_multicast = rtl8180_prepare_multicast, .configure_filter = rtl8180_configure_filter, }; diff --git a/drivers/net/wireless/rtl818x/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c index 53f57dc52226..90f38357393c 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c @@ -1192,10 +1192,16 @@ static void rtl8187_bss_info_changed(struct ieee80211_hw *dev, info->use_short_preamble); } +static u64 rtl8187_prepare_multicast(struct ieee80211_hw *dev, + int mc_count, struct dev_addr_list *mc_list) +{ + return mc_count; +} + static void rtl8187_configure_filter(struct ieee80211_hw *dev, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, struct dev_addr_list *mclist) + u64 multicast) { struct rtl8187_priv *priv = dev->priv; @@ -1205,7 +1211,7 @@ static void rtl8187_configure_filter(struct ieee80211_hw *dev, priv->rx_conf ^= RTL818X_RX_CONF_CTRL; if (changed_flags & FIF_OTHER_BSS) priv->rx_conf ^= RTL818X_RX_CONF_MONITOR; - if (*total_flags & FIF_ALLMULTI || mc_count > 0) + if (*total_flags & FIF_ALLMULTI || multicast > 0) priv->rx_conf |= RTL818X_RX_CONF_MULTICAST; else priv->rx_conf &= ~RTL818X_RX_CONF_MULTICAST; @@ -1268,6 +1274,7 @@ static const struct ieee80211_ops rtl8187_ops = { .remove_interface = rtl8187_remove_interface, .config = rtl8187_config, .bss_info_changed = rtl8187_bss_info_changed, + .prepare_multicast = rtl8187_prepare_multicast, .configure_filter = rtl8187_configure_filter, .conf_tx = rtl8187_conf_tx }; diff --git a/drivers/net/wireless/wl12xx/wl1251_main.c b/drivers/net/wireless/wl12xx/wl1251_main.c index 7148934e464d..5809ef5b18f8 100644 --- a/drivers/net/wireless/wl12xx/wl1251_main.c +++ b/drivers/net/wireless/wl12xx/wl1251_main.c @@ -652,9 +652,7 @@ out: static void wl1251_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed, - unsigned int *total, - int mc_count, - struct dev_addr_list *mc_list) + unsigned int *total,u64 multicast) { struct wl1251 *wl = hw->priv; diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index 4102d590b798..754be8179307 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -793,9 +793,7 @@ out: static void wl1271_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed, - unsigned int *total, - int mc_count, - struct dev_addr_list *mc_list) + unsigned int *total,u64 multicast) { struct wl1271 *wl = hw->priv; diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c index 55b7fbdc85dc..6d666359a42f 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zd1211rw/zd_mac.c @@ -796,18 +796,40 @@ static void set_rx_filter_handler(struct work_struct *work) dev_err(zd_mac_dev(mac), "set_rx_filter_handler error %d\n", r); } +static u64 zd_op_prepare_multicast(struct ieee80211_hw *hw, + int mc_count, struct dev_addr_list *mclist) +{ + struct zd_mac *mac = zd_hw_mac(hw); + struct zd_mc_hash hash; + int i; + + zd_mc_clear(&hash); + + for (i = 0; i < mc_count; i++) { + if (!mclist) + break; + dev_dbg_f(zd_mac_dev(mac), "mc addr %pM\n", mclist->dmi_addr); + zd_mc_add_addr(&hash, mclist->dmi_addr); + mclist = mclist->next; + } + + return hash.low | ((u64)hash.high << 32); +} + #define SUPPORTED_FIF_FLAGS \ (FIF_PROMISC_IN_BSS | FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \ FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC) static void zd_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *new_flags, - int mc_count, struct dev_mc_list *mclist) + u64 multicast) { - struct zd_mc_hash hash; + struct zd_mc_hash hash = { + .low = multicast, + .high = multicast >> 32, + }; struct zd_mac *mac = zd_hw_mac(hw); unsigned long flags; - int i; /* Only deal with supported flags */ changed_flags &= SUPPORTED_FIF_FLAGS; @@ -819,25 +841,16 @@ static void zd_op_configure_filter(struct ieee80211_hw *hw, if (!changed_flags) return; - if (*new_flags & (FIF_PROMISC_IN_BSS | FIF_ALLMULTI)) { + if (*new_flags & (FIF_PROMISC_IN_BSS | FIF_ALLMULTI)) zd_mc_add_all(&hash); - } else { - zd_mc_clear(&hash); - for (i = 0; i < mc_count; i++) { - if (!mclist) - break; - dev_dbg_f(zd_mac_dev(mac), "mc addr %pM\n", - mclist->dmi_addr); - zd_mc_add_addr(&hash, mclist->dmi_addr); - mclist = mclist->next; - } - } spin_lock_irqsave(&mac->lock, flags); mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL); mac->pass_ctrl = !!(*new_flags & FIF_CONTROL); mac->multicast_hash = hash; spin_unlock_irqrestore(&mac->lock, flags); + + /* XXX: these can be called here now, can sleep now! */ queue_work(zd_workqueue, &mac->set_multicast_hash_work); if (changed_flags & FIF_CONTROL) @@ -940,6 +953,7 @@ static const struct ieee80211_ops zd_ops = { .add_interface = zd_op_add_interface, .remove_interface = zd_op_remove_interface, .config = zd_op_config, + .prepare_multicast = zd_op_prepare_multicast, .configure_filter = zd_op_configure_filter, .bss_info_changed = zd_op_bss_info_changed, .get_tsf = zd_op_get_tsf, diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 76d43e12cc29..bc865206e5f1 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1219,10 +1219,13 @@ ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw, * the driver's configure_filter() function which frames should be * passed to mac80211 and which should be filtered out. * - * The configure_filter() callback is invoked with the parameters - * @mc_count and @mc_list for the combined multicast address list - * of all virtual interfaces, @changed_flags telling which flags - * were changed and @total_flags with the new flag states. + * Before configure_filter() is invoked, the prepare_multicast() + * callback is invoked with the parameters @mc_count and @mc_list + * for the combined multicast address list of all virtual interfaces. + * It's use is optional, and it returns a u64 that is passed to + * configure_filter(). Additionally, configure_filter() has the + * arguments @changed_flags telling which flags were changed and + * @total_flags with the new flag states. * * If your device has no multicast address filters your driver will * need to check both the %FIF_ALLMULTI flag and the @mc_count @@ -1375,9 +1378,13 @@ enum ieee80211_ampdu_mlme_action { * for association indication. The @changed parameter indicates which * of the bss parameters has changed when a call is made. * + * @prepare_multicast: Prepare for multicast filter configuration. + * This callback is optional, and its return value is passed + * to configure_filter(). This callback must be atomic. + * * @configure_filter: Configure the device's RX filter. * See the section "Frame filtering" for more information. - * This callback must be implemented and atomic. + * This callback must be implemented. * * @set_tim: Set TIM bit. mac80211 calls this function when a TIM bit * must be set or cleared for a given STA. Must be atomic. @@ -1479,10 +1486,12 @@ struct ieee80211_ops { struct ieee80211_vif *vif, struct ieee80211_bss_conf *info, u32 changed); + u64 (*prepare_multicast)(struct ieee80211_hw *hw, + int mc_count, struct dev_addr_list *mc_list); void (*configure_filter)(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, struct dev_addr_list *mc_list); + u64 multicast); int (*set_tim)(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set); int (*set_key)(struct ieee80211_hw *hw, enum set_key_cmd cmd, diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 4100c361a99d..d231c9323ad1 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -55,16 +55,32 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local, trace_drv_bss_info_changed(local, vif, info, changed); } +static inline u64 drv_prepare_multicast(struct ieee80211_local *local, + int mc_count, + struct dev_addr_list *mc_list) +{ + u64 ret = 0; + + if (local->ops->prepare_multicast) + ret = local->ops->prepare_multicast(&local->hw, mc_count, + mc_list); + + trace_drv_prepare_multicast(local, mc_count, ret); + + return ret; +} + static inline void drv_configure_filter(struct ieee80211_local *local, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, - struct dev_addr_list *mc_list) + u64 multicast) { + might_sleep(); + local->ops->configure_filter(&local->hw, changed_flags, total_flags, - mc_count, mc_list); + multicast); trace_drv_configure_filter(local, changed_flags, total_flags, - mc_count); + multicast); } static inline int drv_set_tim(struct ieee80211_local *local, diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index 5a10da2d70fd..37b9051afcf3 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -191,31 +191,55 @@ TRACE_EVENT(drv_bss_info_changed, ) ); +TRACE_EVENT(drv_prepare_multicast, + TP_PROTO(struct ieee80211_local *local, int mc_count, u64 ret), + + TP_ARGS(local, mc_count, ret), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(int, mc_count) + __field(u64, ret) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->mc_count = mc_count; + __entry->ret = ret; + ), + + TP_printk( + LOCAL_PR_FMT " prepare mc (%d): %llx", + LOCAL_PR_ARG, __entry->mc_count, + (unsigned long long) __entry->ret + ) +); + TRACE_EVENT(drv_configure_filter, TP_PROTO(struct ieee80211_local *local, unsigned int changed_flags, unsigned int *total_flags, - int mc_count), + u64 multicast), - TP_ARGS(local, changed_flags, total_flags, mc_count), + TP_ARGS(local, changed_flags, total_flags, multicast), TP_STRUCT__entry( LOCAL_ENTRY __field(unsigned int, changed) __field(unsigned int, total) - __field(int, mc) + __field(u64, multicast) ), TP_fast_assign( LOCAL_ASSIGN; __entry->changed = changed_flags; __entry->total = *total_flags; - __entry->mc = mc_count; + __entry->multicast = multicast; ), TP_printk( - LOCAL_PR_FMT " changed:%#x total:%#x mc:%d", - LOCAL_PR_ARG, __entry->changed, __entry->total, __entry->mc + LOCAL_PR_FMT " changed:%#x total:%#x", + LOCAL_PR_ARG, __entry->changed, __entry->total ) ); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index a6abc7dfd903..a07f01736a91 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -636,6 +636,9 @@ struct ieee80211_local { /* protects the aggregated multicast list and filter calls */ spinlock_t filter_lock; + /* used for uploading changed mc list */ + struct work_struct reconfig_filter; + /* aggregated multicast list */ struct dev_addr_list *mc_list; int mc_count; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index e8fb03b91a44..b161301056df 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -227,9 +227,7 @@ static int ieee80211_open(struct net_device *dev) if (sdata->u.mntr_flags & MONITOR_FLAG_OTHER_BSS) local->fif_other_bss++; - spin_lock_bh(&local->filter_lock); ieee80211_configure_filter(local); - spin_unlock_bh(&local->filter_lock); break; default: conf.vif = &sdata->vif; @@ -241,17 +239,13 @@ static int ieee80211_open(struct net_device *dev) if (ieee80211_vif_is_mesh(&sdata->vif)) { local->fif_other_bss++; - spin_lock_bh(&local->filter_lock); ieee80211_configure_filter(local); - spin_unlock_bh(&local->filter_lock); ieee80211_start_mesh(sdata); } else if (sdata->vif.type == NL80211_IFTYPE_AP) { local->fif_pspoll++; - spin_lock_bh(&local->filter_lock); ieee80211_configure_filter(local); - spin_unlock_bh(&local->filter_lock); } changed |= ieee80211_reset_erp_info(sdata); @@ -404,10 +398,11 @@ static int ieee80211_stop(struct net_device *dev) spin_lock_bh(&local->filter_lock); __dev_addr_unsync(&local->mc_list, &local->mc_count, &dev->mc_list, &dev->mc_count); - ieee80211_configure_filter(local); spin_unlock_bh(&local->filter_lock); netif_addr_unlock_bh(dev); + ieee80211_configure_filter(local); + del_timer_sync(&local->dynamic_ps_timer); cancel_work_sync(&local->dynamic_ps_enable_work); @@ -458,9 +453,7 @@ static int ieee80211_stop(struct net_device *dev) if (sdata->u.mntr_flags & MONITOR_FLAG_OTHER_BSS) local->fif_other_bss--; - spin_lock_bh(&local->filter_lock); ieee80211_configure_filter(local); - spin_unlock_bh(&local->filter_lock); break; case NL80211_IFTYPE_STATION: del_timer_sync(&sdata->u.mgd.chswitch_timer); @@ -503,9 +496,7 @@ static int ieee80211_stop(struct net_device *dev) local->fif_other_bss--; atomic_dec(&local->iff_allmultis); - spin_lock_bh(&local->filter_lock); ieee80211_configure_filter(local); - spin_unlock_bh(&local->filter_lock); ieee80211_stop_mesh(sdata); } @@ -622,8 +613,8 @@ static void ieee80211_set_multicast_list(struct net_device *dev) spin_lock_bh(&local->filter_lock); __dev_addr_sync(&local->mc_list, &local->mc_count, &dev->mc_list, &dev->mc_count); - ieee80211_configure_filter(local); spin_unlock_bh(&local->filter_lock); + ieee80211_queue_work(&local->hw, &local->reconfig_filter); } /* diff --git a/net/mac80211/main.c b/net/mac80211/main.c index b03fd84777fa..05f923575fee 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -50,9 +50,9 @@ struct ieee80211_tx_status_rtap_hdr { } __attribute__ ((packed)); -/* must be called under mdev tx lock */ void ieee80211_configure_filter(struct ieee80211_local *local) { + u64 mc; unsigned int changed_flags; unsigned int new_flags = 0; @@ -62,7 +62,7 @@ void ieee80211_configure_filter(struct ieee80211_local *local) if (atomic_read(&local->iff_allmultis)) new_flags |= FIF_ALLMULTI; - if (local->monitors) + if (local->monitors || local->scanning) new_flags |= FIF_BCN_PRBRESP_PROMISC; if (local->fif_fcsfail) @@ -80,20 +80,30 @@ void ieee80211_configure_filter(struct ieee80211_local *local) if (local->fif_pspoll) new_flags |= FIF_PSPOLL; + spin_lock_bh(&local->filter_lock); changed_flags = local->filter_flags ^ new_flags; + mc = drv_prepare_multicast(local, local->mc_count, local->mc_list); + spin_unlock_bh(&local->filter_lock); + /* be a bit nasty */ new_flags |= (1<<31); - drv_configure_filter(local, changed_flags, &new_flags, - local->mc_count, - local->mc_list); + drv_configure_filter(local, changed_flags, &new_flags, mc); WARN_ON(new_flags & (1<<31)); local->filter_flags = new_flags & ~(1<<31); } +static void ieee80211_reconfig_filter(struct work_struct *work) +{ + struct ieee80211_local *local = + container_of(work, struct ieee80211_local, reconfig_filter); + + ieee80211_configure_filter(local); +} + int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) { struct ieee80211_channel *chan, *scan_chan; @@ -692,6 +702,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, INIT_WORK(&local->restart_work, ieee80211_restart_work); + INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter); + INIT_WORK(&local->dynamic_ps_enable_work, ieee80211_dynamic_ps_enable_work); INIT_WORK(&local->dynamic_ps_disable_work, @@ -946,6 +958,8 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) rtnl_unlock(); + cancel_work_sync(&local->reconfig_filter); + ieee80211_clear_tx_pending(local); sta_info_stop(local); rate_control_deinitialize(local); diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index e091cbc3434f..1e04be6b9129 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -292,13 +292,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) if (was_hw_scan) goto done; - spin_lock_bh(&local->filter_lock); - local->filter_flags &= ~FIF_BCN_PRBRESP_PROMISC; - drv_configure_filter(local, FIF_BCN_PRBRESP_PROMISC, - &local->filter_flags, - local->mc_count, - local->mc_list); - spin_unlock_bh(&local->filter_lock); + ieee80211_configure_filter(local); drv_sw_scan_complete(local); @@ -376,13 +370,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) local->next_scan_state = SCAN_DECISION; local->scan_channel_idx = 0; - spin_lock_bh(&local->filter_lock); - local->filter_flags |= FIF_BCN_PRBRESP_PROMISC; - drv_configure_filter(local, FIF_BCN_PRBRESP_PROMISC, - &local->filter_flags, - local->mc_count, - local->mc_list); - spin_unlock_bh(&local->filter_lock); + ieee80211_configure_filter(local); /* TODO: start scan as soon as all nullfunc frames are ACKed */ ieee80211_queue_delayed_work(&local->hw, diff --git a/net/mac80211/util.c b/net/mac80211/util.c index e55d57f559ec..5eb306377c63 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1076,9 +1076,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) /* reconfigure hardware */ ieee80211_hw_config(local, ~0); - spin_lock_bh(&local->filter_lock); ieee80211_configure_filter(local); - spin_unlock_bh(&local->filter_lock); /* Finally also reconfigure all the BSS information */ list_for_each_entry(sdata, &local->interfaces, list) { -- cgit v1.2.3-58-ga151 From f424afa17899408cbd267a4c4534ca6fc9d8f71c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 17 Aug 2009 16:18:07 +0200 Subject: mac80211: remove deprecated API All but two drivers have now stopped using the two deprecated members radio_enabled and beacon_int, so it's about time to remove them for good. Signed-off-by: Johannes Berg Acked-by: Kalle Valo Signed-off-by: John W. Linville --- drivers/net/wireless/at76c50x-usb.c | 5 ++--- drivers/net/wireless/mwl8k.c | 7 ------- include/net/mac80211.h | 15 --------------- net/mac80211/main.c | 4 ---- 4 files changed, 2 insertions(+), 29 deletions(-) (limited to 'net') diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c index a6e19545ac6a..8e1a55dec351 100644 --- a/drivers/net/wireless/at76c50x-usb.c +++ b/drivers/net/wireless/at76c50x-usb.c @@ -1950,9 +1950,8 @@ static int at76_config(struct ieee80211_hw *hw, u32 changed) { struct at76_priv *priv = hw->priv; - at76_dbg(DBG_MAC80211, "%s(): channel %d radio %d", - __func__, hw->conf.channel->hw_value, - hw->conf.radio_enabled); + at76_dbg(DBG_MAC80211, "%s(): channel %d", + __func__, hw->conf.channel->hw_value); at76_dbg_dump(DBG_MAC80211, priv->bssid, ETH_ALEN, "bssid:"); mutex_lock(&priv->mtx); diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index f84387083e73..c32e93c8c410 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -3031,13 +3031,6 @@ static int mwl8k_config_wt(struct work_struct *wt) struct mwl8k_priv *priv = hw->priv; int rc = 0; - if (!conf->radio_enabled) { - mwl8k_cmd_802_11_radio_control(hw, MWL8K_RADIO_DISABLE); - priv->current_channel = NULL; - rc = 0; - goto mwl8k_config_exit; - } - if (mwl8k_cmd_802_11_radio_control(hw, MWL8K_RADIO_ENABLE)) { rc = -EINVAL; goto mwl8k_config_exit; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index bc865206e5f1..aac84d7bd46e 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -583,7 +583,6 @@ enum ieee80211_conf_flags { /** * enum ieee80211_conf_changed - denotes which configuration changed * - * @_IEEE80211_CONF_CHANGE_RADIO_ENABLED: DEPRECATED * @IEEE80211_CONF_CHANGE_LISTEN_INTERVAL: the listen interval changed * @IEEE80211_CONF_CHANGE_RADIOTAP: the radiotap flag changed * @IEEE80211_CONF_CHANGE_PS: the PS flag or dynamic PS timeout changed @@ -593,7 +592,6 @@ enum ieee80211_conf_flags { * @IEEE80211_CONF_CHANGE_IDLE: Idle flag changed */ enum ieee80211_conf_changed { - _IEEE80211_CONF_CHANGE_RADIO_ENABLED = BIT(0), IEEE80211_CONF_CHANGE_LISTEN_INTERVAL = BIT(2), IEEE80211_CONF_CHANGE_RADIOTAP = BIT(3), IEEE80211_CONF_CHANGE_PS = BIT(4), @@ -603,14 +601,6 @@ enum ieee80211_conf_changed { IEEE80211_CONF_CHANGE_IDLE = BIT(8), }; -static inline __deprecated enum ieee80211_conf_changed -__IEEE80211_CONF_CHANGE_RADIO_ENABLED(void) -{ - return _IEEE80211_CONF_CHANGE_RADIO_ENABLED; -} -#define IEEE80211_CONF_CHANGE_RADIO_ENABLED \ - __IEEE80211_CONF_CHANGE_RADIO_ENABLED() - /** * struct ieee80211_conf - configuration of the device * @@ -618,9 +608,6 @@ __IEEE80211_CONF_CHANGE_RADIO_ENABLED(void) * * @flags: configuration flags defined above * - * @radio_enabled: when zero, driver is required to switch off the radio. - * @beacon_int: DEPRECATED, DO NOT USE - * * @listen_interval: listen interval in units of beacon interval * @max_sleep_period: the maximum number of beacon intervals to sleep for * before checking the beacon for a TIM bit (managed mode only); this @@ -644,13 +631,11 @@ __IEEE80211_CONF_CHANGE_RADIO_ENABLED(void) * number of transmissions not the number of retries */ struct ieee80211_conf { - int __deprecated beacon_int; u32 flags; int power_level, dynamic_ps_timeout; int max_sleep_period; u16 listen_interval; - bool __deprecated radio_enabled; u8 long_frame_max_tx_count, short_frame_max_tx_count; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 05f923575fee..3302df96f8d4 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -241,9 +241,6 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, drv_bss_info_changed(local, &sdata->vif, &sdata->vif.bss_conf, changed); - - /* DEPRECATED */ - local->hw.conf.beacon_int = sdata->vif.bss_conf.beacon_int; } u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata) @@ -687,7 +684,6 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, local->hw.max_rates = 1; local->hw.conf.long_frame_max_tx_count = wiphy->retry_long; local->hw.conf.short_frame_max_tx_count = wiphy->retry_short; - local->hw.conf.radio_enabled = true; local->user_power_level = -1; INIT_LIST_HEAD(&local->interfaces); -- cgit v1.2.3-58-ga151 From bfc32e6a9559d3e30925929cd9a9df7498f325db Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Mon, 17 Aug 2009 17:15:55 -0700 Subject: mac80211: Decouple fail_avg stats used by mesh from rate control algorithm. Mesh uses the tx failure average to compute the (m)path metric. This used to be done inside the rate control module. This patch breaks the dependency between the mesh stack and the rate control algorithm. Mesh will now work independently of the chosen rate control algorithm. The mesh stack keeps a moving average of the average transmission losses for each mesh peer station. If the fail average exceeds a certain threshold, the peer link is marked as broken. Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- net/mac80211/main.c | 2 ++ net/mac80211/mesh.h | 2 ++ net/mac80211/mesh_hwmp.c | 18 ++++++++++++++++++ net/mac80211/rc80211_minstrel.c | 16 +--------------- net/mac80211/rc80211_pid_algo.c | 15 +-------------- 5 files changed, 24 insertions(+), 29 deletions(-) (limited to 'net') diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 3302df96f8d4..f80efd7ff5ec 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -482,6 +482,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) } rate_control_tx_status(local, sband, sta, skb); + if (ieee80211_vif_is_mesh(&sta->sdata->vif)) + ieee80211s_update_metric(local, sta, skb); } rcu_read_unlock(); diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index eb23fc639b2b..dd1c19319f0a 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -226,6 +226,8 @@ void mesh_mgmt_ies_add(struct sk_buff *skb, void mesh_rmc_free(struct ieee80211_sub_if_data *sdata); int mesh_rmc_init(struct ieee80211_sub_if_data *sdata); void ieee80211s_init(void); +void ieee80211s_update_metric(struct ieee80211_local *local, + struct sta_info *stainfo, struct sk_buff *skb); void ieee80211s_stop(void); void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata); ieee80211_rx_result diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index ef1efd362691..7aeba00ac502 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -201,6 +201,24 @@ int mesh_path_error_tx(u8 *dst, __le32 dst_dsn, u8 *ra, return 0; } +void ieee80211s_update_metric(struct ieee80211_local *local, + struct sta_info *stainfo, struct sk_buff *skb) +{ + struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + int failed; + + if (!ieee80211_is_data(hdr->frame_control)) + return; + + failed = !(txinfo->flags & IEEE80211_TX_STAT_ACK); + + /* moving average, scaled to 100 */ + stainfo->fail_avg = ((80 * stainfo->fail_avg + 5) / 100 + 20 * failed); + if (stainfo->fail_avg > 95) + mesh_plink_broken(stainfo); +} + static u32 airtime_link_metric_get(struct ieee80211_local *local, struct sta_info *sta) { diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index 007164919e02..7c5142988bbb 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c @@ -51,7 +51,6 @@ #include #include #include -#include "mesh.h" #include "rate.h" #include "rc80211_minstrel.h" @@ -156,16 +155,12 @@ minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband, struct sk_buff *skb) { struct minstrel_sta_info *mi = priv_sta; - struct minstrel_priv *mp = (struct minstrel_priv *)priv; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_tx_rate *ar = info->status.rates; - struct ieee80211_local *local = hw_to_local(mp->hw); - struct sta_info *si; int i, ndx; int success; success = !!(info->flags & IEEE80211_TX_STAT_ACK); - si = sta_info_get(local, sta->addr); for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { if (ar[i].idx < 0) @@ -177,17 +172,8 @@ minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband, mi->r[ndx].attempts += ar[i].count; - if ((i != IEEE80211_TX_MAX_RATES - 1) && (ar[i + 1].idx < 0)) { + if ((i != IEEE80211_TX_MAX_RATES - 1) && (ar[i + 1].idx < 0)) mi->r[ndx].success += success; - if (si) { - si->fail_avg = (18050 - mi->r[ndx].probability) - / 180; - WARN_ON(si->fail_avg > 100); - if (si->fail_avg == 100 && - ieee80211_vif_is_mesh(&si->sdata->vif)) - mesh_plink_broken(si); - } - } } if ((info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) && (i >= 0)) diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c index 8c053be9dc24..f6e25d7d957c 100644 --- a/net/mac80211/rc80211_pid_algo.c +++ b/net/mac80211/rc80211_pid_algo.c @@ -169,19 +169,9 @@ static void rate_control_pid_sample(struct rc_pid_info *pinfo, * still a good measurement and copy it. */ if (unlikely(spinfo->tx_num_xmit == 0)) pf = spinfo->last_pf; - else { - /* XXX: BAD HACK!!! */ - struct sta_info *si = container_of(sta, struct sta_info, sta); - + else pf = spinfo->tx_num_failed * 100 / spinfo->tx_num_xmit; - if (ieee80211_vif_is_mesh(&si->sdata->vif) && pf == 100) - mesh_plink_broken(si); - pf <<= RC_PID_ARITH_SHIFT; - si->fail_avg = ((pf + (spinfo->last_pf << 3)) / 9) - >> RC_PID_ARITH_SHIFT; - } - spinfo->tx_num_xmit = 0; spinfo->tx_num_failed = 0; @@ -348,9 +338,6 @@ rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband, } spinfo->txrate_idx = rate_lowest_index(sband, sta); - /* HACK */ - si = container_of(sta, struct sta_info, sta); - si->fail_avg = 0; } static void *rate_control_pid_alloc(struct ieee80211_hw *hw, -- cgit v1.2.3-58-ga151 From 29508d122a5228c2a68d1e9a39251d3991b3cfef Mon Sep 17 00:00:00 2001 From: "John W. Linville" Date: Tue, 18 Aug 2009 10:46:42 -0400 Subject: rc80211_pid_algo.c: remove unused variable declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CC [M] net/mac80211/rc80211_pid_algo.o net/mac80211/rc80211_pid_algo.c: In function ‘rate_control_pid_rate_init’: net/mac80211/rc80211_pid_algo.c:304: warning: unused variable ‘si’ Signed-off-by: John W. Linville --- net/mac80211/rc80211_pid_algo.c | 1 - 1 file changed, 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c index f6e25d7d957c..699d3ed869c4 100644 --- a/net/mac80211/rc80211_pid_algo.c +++ b/net/mac80211/rc80211_pid_algo.c @@ -301,7 +301,6 @@ rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband, struct rc_pid_sta_info *spinfo = priv_sta; struct rc_pid_info *pinfo = priv; struct rc_pid_rateinfo *rinfo = pinfo->rinfo; - struct sta_info *si; int i, j, tmp; bool s; -- cgit v1.2.3-58-ga151 From c8a61a7d33350eeec668fc6230ad55f5fa93209b Mon Sep 17 00:00:00 2001 From: Daniel Walker Date: Tue, 18 Aug 2009 10:59:00 -0700 Subject: mac80211: New stat counters for multicast and unicast forwarded frames This expands on the current fwded_frames stat counter which should be equal to the total of these two new counters. The new counters are called "fwded_mcast" and "fwded_unicast". Signed-off-by: Daniel Walker Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- net/mac80211/debugfs_netdev.c | 6 ++++++ net/mac80211/ieee80211_i.h | 6 +++++- net/mac80211/mesh_hwmp.c | 3 +++ net/mac80211/rx.c | 8 +++++++- 4 files changed, 21 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index e9ec6cae2d39..61234e79022b 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -116,6 +116,8 @@ IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC); #ifdef CONFIG_MAC80211_MESH /* Mesh stats attributes */ +IEEE80211_IF_FILE(fwded_mcast, u.mesh.mshstats.fwded_mcast, DEC); +IEEE80211_IF_FILE(fwded_unicast, u.mesh.mshstats.fwded_unicast, DEC); IEEE80211_IF_FILE(fwded_frames, u.mesh.mshstats.fwded_frames, DEC); IEEE80211_IF_FILE(dropped_frames_ttl, u.mesh.mshstats.dropped_frames_ttl, DEC); IEEE80211_IF_FILE(dropped_frames_no_route, @@ -205,6 +207,8 @@ static void add_mesh_stats(struct ieee80211_sub_if_data *sdata) { sdata->mesh_stats_dir = debugfs_create_dir("mesh_stats", sdata->debugfsdir); + MESHSTATS_ADD(fwded_mcast); + MESHSTATS_ADD(fwded_unicast); MESHSTATS_ADD(fwded_frames); MESHSTATS_ADD(dropped_frames_ttl); MESHSTATS_ADD(dropped_frames_no_route); @@ -327,6 +331,8 @@ static void del_monitor_files(struct ieee80211_sub_if_data *sdata) static void del_mesh_stats(struct ieee80211_sub_if_data *sdata) { + MESHSTATS_DEL(fwded_mcast); + MESHSTATS_DEL(fwded_unicast); MESHSTATS_DEL(fwded_frames); MESHSTATS_DEL(dropped_frames_ttl); MESHSTATS_DEL(dropped_frames_no_route); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index a07f01736a91..93e618a980d1 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -212,7 +212,9 @@ struct ieee80211_if_vlan { }; struct mesh_stats { - __u32 fwded_frames; /* Mesh forwarded frames */ + __u32 fwded_mcast; /* Mesh forwarded multicast frames */ + __u32 fwded_unicast; /* Mesh forwarded unicast frames */ + __u32 fwded_frames; /* Mesh total forwarded frames */ __u32 dropped_frames_ttl; /* Not transmitted since mesh_ttl == 0*/ __u32 dropped_frames_no_route; /* Not transmitted, no route found */ atomic_t estab_plinks; @@ -506,6 +508,8 @@ struct ieee80211_sub_if_data { #ifdef CONFIG_MAC80211_MESH struct dentry *mesh_stats_dir; struct { + struct dentry *fwded_mcast; + struct dentry *fwded_unicast; struct dentry *fwded_frames; struct dentry *dropped_frames_ttl; struct dentry *dropped_frames_no_route; diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 7aeba00ac502..e12a786e26b8 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -497,6 +497,7 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, hopcount, ttl, cpu_to_le32(lifetime), cpu_to_le32(metric), cpu_to_le32(preq_id), sdata); + ifmsh->mshstats.fwded_mcast++; ifmsh->mshstats.fwded_frames++; } } @@ -555,6 +556,8 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata, cpu_to_le32(lifetime), cpu_to_le32(metric), 0, sdata); rcu_read_unlock(); + + sdata->u.mesh.mshstats.fwded_unicast++; sdata->u.mesh.mshstats.fwded_frames++; return; diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 4cd9e45b1443..7065fd7e7ba2 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1550,7 +1550,10 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; info->control.vif = &rx->sdata->vif; ieee80211_select_queue(local, fwd_skb); - if (!is_multicast_ether_addr(fwd_hdr->addr1)) { + if (is_multicast_ether_addr(fwd_hdr->addr1)) + IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh, + fwded_mcast); + else { int err; /* * Save TA to addr1 to send TA a path error if a @@ -1564,6 +1567,9 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) * later to the pending skb queue. */ if (err) return RX_DROP_MONITOR; + + IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh, + fwded_unicast); } IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh, fwded_frames); -- cgit v1.2.3-58-ga151 From ad002395fd230528281083f4be71855ed7e35b04 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 18 Aug 2009 19:51:57 +0200 Subject: cfg80211: fix dangling scan request checking My patch "cfg80211: fix deadlock" broke the code it was supposed to fix, the scan request checking. But it's not trivial to put it back the way it was, since the original patch had a deadlock. Now do it in a completely new way: queue the check off to a work struct, where we can freely lock. But that has some more complications, like needing to wait for it to be done before the wiphy/rdev can be destroyed, so some code is required to handle that. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/cfg80211.h | 2 ++ net/wireless/core.c | 76 +++++++++++++++++++++++++++++++++++++++++--------- net/wireless/core.h | 2 ++ 3 files changed, 67 insertions(+), 13 deletions(-) (limited to 'net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 0b146bb2dd14..3d874c620219 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1325,6 +1325,8 @@ struct wireless_dev { struct mutex mtx; + struct work_struct cleanup_work; + /* currently used for IBSS and SME - might be rearranged later */ u8 ssid[IEEE80211_MAX_SSID_LEN]; u8 ssid_len; diff --git a/net/wireless/core.c b/net/wireless/core.c index 69a185ba9ff1..c150071b6f29 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -430,6 +430,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) INIT_WORK(&rdev->conn_work, cfg80211_conn_work); INIT_WORK(&rdev->event_work, cfg80211_event_work); + init_waitqueue_head(&rdev->dev_wait); + /* * Initialize wiphy parameters to IEEE 802.11 MIB default values. * Fragmentation and RTS threshold are disabled by default with the @@ -574,7 +576,23 @@ void wiphy_unregister(struct wiphy *wiphy) /* protect the device list */ mutex_lock(&cfg80211_mutex); + wait_event(rdev->dev_wait, ({ + int __count; + mutex_lock(&rdev->devlist_mtx); + __count = rdev->opencount; + mutex_unlock(&rdev->devlist_mtx); + __count == 0;})); + + mutex_lock(&rdev->devlist_mtx); BUG_ON(!list_empty(&rdev->netdev_list)); + mutex_unlock(&rdev->devlist_mtx); + + /* + * First remove the hardware from everywhere, this makes + * it impossible to find from userspace. + */ + cfg80211_debugfs_rdev_del(rdev); + list_del(&rdev->list); /* * Try to grab rdev->mtx. If a command is still in progress, @@ -582,26 +600,18 @@ void wiphy_unregister(struct wiphy *wiphy) * down the device already. We wait for this command to complete * before unlinking the item from the list. * Note: as codified by the BUG_ON above we cannot get here if - * a virtual interface is still associated. Hence, we can only - * get to lock contention here if userspace issues a command - * that identified the hardware by wiphy index. + * a virtual interface is still present. Hence, we can only get + * to lock contention here if userspace issues a command that + * identified the hardware by wiphy index. */ cfg80211_lock_rdev(rdev); - - if (WARN_ON(rdev->scan_req)) { - rdev->scan_req->aborted = true; - ___cfg80211_scan_done(rdev); - } - + /* nothing */ cfg80211_unlock_rdev(rdev); - cfg80211_debugfs_rdev_del(rdev); - /* If this device got a regulatory hint tell core its * free to listen now to a new shiny device regulatory hint */ reg_device_remove(wiphy); - list_del(&rdev->list); cfg80211_rdev_list_generation++; device_del(&rdev->wiphy.dev); debugfs_remove(rdev->wiphy.debugfsdir); @@ -640,6 +650,31 @@ void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked) } EXPORT_SYMBOL(wiphy_rfkill_set_hw_state); +static void wdev_cleanup_work(struct work_struct *work) +{ + struct wireless_dev *wdev; + struct cfg80211_registered_device *rdev; + + wdev = container_of(work, struct wireless_dev, cleanup_work); + rdev = wiphy_to_dev(wdev->wiphy); + + cfg80211_lock_rdev(rdev); + + if (WARN_ON(rdev->scan_req && rdev->scan_req->dev == wdev->netdev)) { + rdev->scan_req->aborted = true; + ___cfg80211_scan_done(rdev); + } + + cfg80211_unlock_rdev(rdev); + + mutex_lock(&rdev->devlist_mtx); + rdev->opencount--; + mutex_unlock(&rdev->devlist_mtx); + wake_up(&rdev->dev_wait); + + dev_put(wdev->netdev); +} + static int cfg80211_netdev_notifier_call(struct notifier_block * nb, unsigned long state, void *ndev) @@ -663,6 +698,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, * are added with nl80211. */ mutex_init(&wdev->mtx); + INIT_WORK(&wdev->cleanup_work, wdev_cleanup_work); INIT_LIST_HEAD(&wdev->event_list); spin_lock_init(&wdev->event_lock); mutex_lock(&rdev->devlist_mtx); @@ -717,8 +753,22 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, default: break; } + dev_hold(dev); + schedule_work(&wdev->cleanup_work); break; case NETDEV_UP: + /* + * If we have a really quick DOWN/UP succession we may + * have this work still pending ... cancel it and see + * if it was pending, in which case we need to account + * for some of the work it would have done. + */ + if (cancel_work_sync(&wdev->cleanup_work)) { + mutex_lock(&rdev->devlist_mtx); + rdev->opencount--; + mutex_unlock(&rdev->devlist_mtx); + dev_put(dev); + } #ifdef CONFIG_WIRELESS_EXT cfg80211_lock_rdev(rdev); mutex_lock(&rdev->devlist_mtx); @@ -734,6 +784,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, break; } wdev_unlock(wdev); + rdev->opencount++; mutex_unlock(&rdev->devlist_mtx); cfg80211_unlock_rdev(rdev); #endif @@ -756,7 +807,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, sysfs_remove_link(&dev->dev.kobj, "phy80211"); list_del_init(&wdev->list); rdev->devlist_generation++; - mutex_destroy(&wdev->mtx); #ifdef CONFIG_WIRELESS_EXT kfree(wdev->wext.keys); #endif diff --git a/net/wireless/core.h b/net/wireless/core.h index c603f5286326..f565432ae22f 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -50,6 +50,8 @@ struct cfg80211_registered_device { struct mutex devlist_mtx; struct list_head netdev_list; int devlist_generation; + int opencount; /* also protected by devlist_mtx */ + wait_queue_head_t dev_wait; /* BSSes/scanning */ spinlock_t bss_lock; -- cgit v1.2.3-58-ga151 From 415ad1efae1d5fe00d739e612d262eabda90f5e8 Mon Sep 17 00:00:00 2001 From: David Kilroy Date: Wed, 19 Aug 2009 00:43:31 +0100 Subject: cfg80211: fix leaks of wdev->conn->ie This only occurs in the following error situations: - driver calls connect_result with failure - error scheduling authentication on connect - error initiating scan (to get BSSID and channel) on connect - userspace calls disconnect while in the SCANNING or SCAN_AGAIN states Signed-off-by: David Kilroy Reviewed-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/sme.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'net') diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 6fb6a704ca40..9ddc00e3c239 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -395,6 +395,8 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, if (status != WLAN_STATUS_SUCCESS) { wdev->sme_state = CFG80211_SME_IDLE; + if (wdev->conn) + kfree(wdev->conn->ie); kfree(wdev->conn); wdev->conn = NULL; kfree(wdev->connect_keys); @@ -779,6 +781,7 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, } } if (err) { + kfree(wdev->conn->ie); kfree(wdev->conn); wdev->conn = NULL; wdev->sme_state = CFG80211_SME_IDLE; @@ -848,6 +851,7 @@ int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, (wdev->conn->state == CFG80211_CONN_SCANNING || wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)) { wdev->sme_state = CFG80211_SME_IDLE; + kfree(wdev->conn->ie); kfree(wdev->conn); wdev->conn = NULL; wdev->ssid_len = 0; -- cgit v1.2.3-58-ga151 From 16a832e785820aa199641c77b2d6f4a443d2ec46 Mon Sep 17 00:00:00 2001 From: Zhu Yi Date: Wed, 19 Aug 2009 16:08:22 +0800 Subject: cfg80211: allow cfg80211_connect_result with bssid == NULL In case of connection failure, the bssid info is not a must have. Signed-off-by: Zhu Yi Acked-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/core.c | 4 +++- net/wireless/sme.c | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/wireless/core.c b/net/wireless/core.c index c150071b6f29..154e1e294cb9 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -309,7 +310,8 @@ static void cfg80211_process_events(struct wireless_dev *wdev) switch (ev->type) { case EVENT_CONNECT_RESULT: __cfg80211_connect_result( - wdev->netdev, ev->cr.bssid, + wdev->netdev, is_zero_ether_addr(ev->cr.bssid) ? + NULL : ev->cr.bssid, ev->cr.req_ie, ev->cr.req_ie_len, ev->cr.resp_ie, ev->cr.resp_ie_len, ev->cr.status, diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 9ddc00e3c239..4a8289f9b4f0 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -450,7 +450,8 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, return; ev->type = EVENT_CONNECT_RESULT; - memcpy(ev->cr.bssid, bssid, ETH_ALEN); + if (bssid) + memcpy(ev->cr.bssid, bssid, ETH_ALEN); ev->cr.req_ie = ((u8 *)ev) + sizeof(*ev); ev->cr.req_ie_len = req_ie_len; memcpy((void *)ev->cr.req_ie, req_ie, req_ie_len); -- cgit v1.2.3-58-ga151 From 11ba964d4f936609a04e8b9f2051f6027ef761ae Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 19 Aug 2009 19:45:50 +0200 Subject: mac80211: fix register_hw error path "cfg80211: fix alignment problem in scan request" introduced a bug into the error path, because now we allocate the entire scan request and not just the channel list (the channel list is allocated together with the scan request) -- on errors we thus also need to free the entire scan request. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/main.c b/net/mac80211/main.c index f80efd7ff5ec..dd3b0816614d 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -930,7 +930,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) fail_workqueue: wiphy_unregister(local->hw.wiphy); fail_wiphy_register: - kfree(local->int_scan_req->channels); + kfree(local->int_scan_req); return result; } EXPORT_SYMBOL(ieee80211_register_hw); -- cgit v1.2.3-58-ga151