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 c449174..c12029b 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.1 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 bc99e4e..69a185b 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.1 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 da64071..79d2eec 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 8e2ef54..6fb6a70 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.1 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 --- 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 -- 7 files changed, 77 insertions(+), 43 deletions(-) (limited to 'net') diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 4100c36..d231c93 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 5a10da2..37b9051 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 a6abc7d..a07f017 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 e8fb03b..b161301 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 b03fd84..05f9235 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 e091cbc..1e04be6 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 e55d57f..5eb3063 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.1 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 --- net/mac80211/main.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'net') diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 05f9235..3302df9 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.1 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 3302df9..f80efd7 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 eb23fc6..dd1c193 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 ef1efd3..7aeba00 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 0071649..7c51429 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 8c053be..f6e25d7 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.1 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 f6e25d7..699d3ed 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.1 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 e9ec6ca..61234e7 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 a07f017..93e618a 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 7aeba00..e12a786 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 4cd9e45..7065fd7 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.1 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 --- net/wireless/core.c | 76 ++++++++++++++++++++++++++++++++++++++++++++--------- net/wireless/core.h | 2 ++ 2 files changed, 65 insertions(+), 13 deletions(-) (limited to 'net') diff --git a/net/wireless/core.c b/net/wireless/core.c index 69a185b..c150071 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 c603f52..f565432 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.1 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 6fb6a70..9ddc00e 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.1 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 c150071..154e1e2 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 9ddc00e..4a8289f9 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.1 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 f80efd7..dd3b081 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.1