From 22bedad3ce112d5ca1eaf043d4990fa2ed698c87 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 1 Apr 2010 21:22:57 +0000 Subject: net: convert multicast list to list_head Converts the list and the core manipulating with it to be the same as uc_list. +uses two functions for adding/removing mc address (normal and "global" variant) instead of a function parameter. +removes dev_mcast.c completely. +exposes netdev_hw_addr_list_* macros along with __hw_addr_* functions for manipulation with lists on a sandbox (used in bonding and 80211 drivers) Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/mac80211/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/mac80211/main.c') diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 06c33b68d8e5..84ad249a4e2e 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -71,7 +71,7 @@ void ieee80211_configure_filter(struct ieee80211_local *local) spin_lock_bh(&local->filter_lock); changed_flags = local->filter_flags ^ new_flags; - mc = drv_prepare_multicast(local, local->mc_count, local->mc_list); + mc = drv_prepare_multicast(local, &local->mc_list); spin_unlock_bh(&local->filter_lock); /* be a bit nasty */ -- cgit v1.2.3 From 2f787b0b76bf5de2eaa3ca3a29d89123ae03c856 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Sun, 4 Apr 2010 17:59:30 +0000 Subject: mac80211: Ensure initializing private mc_list in prepare_multicast(). Fix kernel panic by NULL pointer dereference in the context of ieee80211_ops->prepare_multicast(). This bug was introduced by commit 22bedad3c.. ("net: convert multicast list to list_head"). Call __hw_addr_init() in ieee80211_alloc_hw() to initialize list_head of private device multicast list, like we do in bond_init(). Signed-off-by: YOSHIFUJI Hideaki Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- net/mac80211/main.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'net/mac80211/main.c') diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 84ad249a4e2e..0b82cd2f781e 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -388,6 +388,9 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, local->uapsd_max_sp_len = IEEE80211_DEFAULT_MAX_SP_LEN; INIT_LIST_HEAD(&local->interfaces); + + __hw_addr_init(&local->mc_list); + mutex_init(&local->iflist_mtx); mutex_init(&local->scan_mtx); -- cgit v1.2.3 From b5878a2dc5e7e7f031a52c3e15b571224cb6b540 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 7 Apr 2010 16:48:40 +0200 Subject: mac80211: enhance tracing Enhance tracing by adding tracing for a variety of callbacks that the drivers call, and also for internal calls (currently limited to queue status). This can aid debugging what is going on in mac80211 in interaction with drivers, since we can now see what drivers call and not just what mac80211 calls in the driver. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/Kconfig | 8 +- net/mac80211/agg-tx.c | 8 ++ net/mac80211/driver-trace.h | 275 ++++++++++++++++++++++++++++++++++++++++++++ net/mac80211/main.c | 2 + net/mac80211/mlme.c | 6 + net/mac80211/scan.c | 2 + net/mac80211/sta_info.c | 2 + net/mac80211/util.c | 4 + 8 files changed, 303 insertions(+), 4 deletions(-) (limited to 'net/mac80211/main.c') diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index 334c359da5e8..8a91f6c0bb18 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -221,8 +221,8 @@ config MAC80211_DRIVER_API_TRACER depends on EVENT_TRACING help Say Y here to make mac80211 register with the ftrace - framework for the driver API -- you can see which - driver methods it is calling then by looking at the - trace. + framework for the driver API -- you can then see which + driver methods it is calling and which API functions + drivers are calling by looking at the trace. - If unsure, say N. + If unsure, say Y. diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 32d2148b5b98..6bb4d0a1e5c5 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -214,6 +214,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) int ret = 0; u16 start_seq_num; + trace_api_start_tx_ba_session(pubsta, tid); + if (WARN_ON(!local->ops->ampdu_action)) return -EINVAL; @@ -440,6 +442,8 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid) struct sta_info *sta; u8 *state; + trace_api_start_tx_ba_cb(sdata, ra, tid); + if (tid >= STA_TID_NUM) { #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "Bad TID value: tid = %d (>= %d)\n", @@ -541,6 +545,8 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; + trace_api_stop_tx_ba_session(pubsta, tid, initiator); + if (!local->ops->ampdu_action) return -EINVAL; @@ -558,6 +564,8 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid) struct sta_info *sta; u8 *state; + trace_api_stop_tx_ba_cb(sdata, ra, tid); + if (tid >= STA_TID_NUM) { #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "Bad TID value: tid = %d (>= %d)\n", diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index 41baf730a5c7..e209cb82ff29 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -32,6 +32,10 @@ static inline void trace_ ## name(proto) {} #define VIF_PR_FMT " vif:%s(%d)" #define VIF_PR_ARG __get_str(vif_name), __entry->vif_type +/* + * Tracing for driver callbacks. + */ + TRACE_EVENT(drv_start, TP_PROTO(struct ieee80211_local *local, int ret), @@ -766,6 +770,277 @@ TRACE_EVENT(drv_flush, LOCAL_PR_ARG, __entry->drop ) ); + +/* + * Tracing for API calls that drivers call. + */ + +TRACE_EVENT(api_start_tx_ba_session, + TP_PROTO(struct ieee80211_sta *sta, u16 tid), + + TP_ARGS(sta, tid), + + TP_STRUCT__entry( + STA_ENTRY + __field(u16, tid) + ), + + TP_fast_assign( + STA_ASSIGN; + __entry->tid = tid; + ), + + TP_printk( + STA_PR_FMT " tid:%d", + STA_PR_ARG, __entry->tid + ) +); + +TRACE_EVENT(api_start_tx_ba_cb, + TP_PROTO(struct ieee80211_sub_if_data *sdata, const u8 *ra, u16 tid), + + TP_ARGS(sdata, ra, tid), + + TP_STRUCT__entry( + VIF_ENTRY + __array(u8, ra, ETH_ALEN) + __field(u16, tid) + ), + + TP_fast_assign( + VIF_ASSIGN; + memcpy(__entry->ra, ra, ETH_ALEN); + __entry->tid = tid; + ), + + TP_printk( + VIF_PR_FMT " ra:%pM tid:%d", + VIF_PR_ARG, __entry->ra, __entry->tid + ) +); + +TRACE_EVENT(api_stop_tx_ba_session, + TP_PROTO(struct ieee80211_sta *sta, u16 tid, u16 initiator), + + TP_ARGS(sta, tid, initiator), + + TP_STRUCT__entry( + STA_ENTRY + __field(u16, tid) + __field(u16, initiator) + ), + + TP_fast_assign( + STA_ASSIGN; + __entry->tid = tid; + __entry->initiator = initiator; + ), + + TP_printk( + STA_PR_FMT " tid:%d initiator:%d", + STA_PR_ARG, __entry->tid, __entry->initiator + ) +); + +TRACE_EVENT(api_stop_tx_ba_cb, + TP_PROTO(struct ieee80211_sub_if_data *sdata, const u8 *ra, u16 tid), + + TP_ARGS(sdata, ra, tid), + + TP_STRUCT__entry( + VIF_ENTRY + __array(u8, ra, ETH_ALEN) + __field(u16, tid) + ), + + TP_fast_assign( + VIF_ASSIGN; + memcpy(__entry->ra, ra, ETH_ALEN); + __entry->tid = tid; + ), + + TP_printk( + VIF_PR_FMT " ra:%pM tid:%d", + VIF_PR_ARG, __entry->ra, __entry->tid + ) +); + +TRACE_EVENT(api_restart_hw, + TP_PROTO(struct ieee80211_local *local), + + TP_ARGS(local), + + TP_STRUCT__entry( + LOCAL_ENTRY + ), + + TP_fast_assign( + LOCAL_ASSIGN; + ), + + TP_printk( + LOCAL_PR_FMT, + LOCAL_PR_ARG + ) +); + +TRACE_EVENT(api_beacon_loss, + TP_PROTO(struct ieee80211_sub_if_data *sdata), + + TP_ARGS(sdata), + + TP_STRUCT__entry( + VIF_ENTRY + ), + + TP_fast_assign( + VIF_ASSIGN; + ), + + TP_printk( + VIF_PR_FMT, + VIF_PR_ARG + ) +); + +TRACE_EVENT(api_connection_loss, + TP_PROTO(struct ieee80211_sub_if_data *sdata), + + TP_ARGS(sdata), + + TP_STRUCT__entry( + VIF_ENTRY + ), + + TP_fast_assign( + VIF_ASSIGN; + ), + + TP_printk( + VIF_PR_FMT, + VIF_PR_ARG + ) +); + +TRACE_EVENT(api_cqm_rssi_notify, + TP_PROTO(struct ieee80211_sub_if_data *sdata, + enum nl80211_cqm_rssi_threshold_event rssi_event), + + TP_ARGS(sdata, rssi_event), + + TP_STRUCT__entry( + VIF_ENTRY + __field(u32, rssi_event) + ), + + TP_fast_assign( + VIF_ASSIGN; + __entry->rssi_event = rssi_event; + ), + + TP_printk( + VIF_PR_FMT " event:%d", + VIF_PR_ARG, __entry->rssi_event + ) +); + +TRACE_EVENT(api_scan_completed, + TP_PROTO(struct ieee80211_local *local, bool aborted), + + TP_ARGS(local, aborted), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(bool, aborted) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->aborted = aborted; + ), + + TP_printk( + LOCAL_PR_FMT " aborted:%d", + LOCAL_PR_ARG, __entry->aborted + ) +); + +TRACE_EVENT(api_sta_block_awake, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sta *sta, bool block), + + TP_ARGS(local, sta, block), + + TP_STRUCT__entry( + LOCAL_ENTRY + STA_ENTRY + __field(bool, block) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + STA_ASSIGN; + __entry->block = block; + ), + + TP_printk( + LOCAL_PR_FMT STA_PR_FMT " block:%d", + LOCAL_PR_ARG, STA_PR_FMT, __entry->block + ) +); + +/* + * Tracing for internal functions + * (which may also be called in response to driver calls) + */ + +TRACE_EVENT(wake_queue, + TP_PROTO(struct ieee80211_local *local, u16 queue, + enum queue_stop_reason reason), + + TP_ARGS(local, queue, reason), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(u16, queue) + __field(u32, reason) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->queue = queue; + __entry->reason = reason; + ), + + TP_printk( + LOCAL_PR_FMT " queue:%d, reason:%d", + LOCAL_PR_ARG, __entry->queue, __entry->reason + ) +); + +TRACE_EVENT(stop_queue, + TP_PROTO(struct ieee80211_local *local, u16 queue, + enum queue_stop_reason reason), + + TP_ARGS(local, queue, reason), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(u16, queue) + __field(u32, reason) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->queue = queue; + __entry->reason = reason; + ), + + TP_printk( + LOCAL_PR_FMT " queue:%d, reason:%d", + LOCAL_PR_ARG, __entry->queue, __entry->reason + ) +); #endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */ #undef TRACE_INCLUDE_PATH diff --git a/net/mac80211/main.c b/net/mac80211/main.c index b887e484ae04..4afe851cf8dc 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -309,6 +309,8 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw) { struct ieee80211_local *local = hw_to_local(hw); + trace_api_restart_hw(local); + /* use this reason, __ieee80211_resume will unblock it */ ieee80211_stop_queues_by_reason(hw, IEEE80211_QUEUE_STOP_REASON_SUSPEND); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 461167dfa42c..d11a54c289a2 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1007,6 +1007,8 @@ void ieee80211_beacon_loss(struct ieee80211_vif *vif) struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); struct ieee80211_hw *hw = &sdata->local->hw; + trace_api_beacon_loss(sdata); + WARN_ON(hw->flags & IEEE80211_HW_CONNECTION_MONITOR); ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work); } @@ -1017,6 +1019,8 @@ void ieee80211_connection_loss(struct ieee80211_vif *vif) struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); struct ieee80211_hw *hw = &sdata->local->hw; + trace_api_connection_loss(sdata); + WARN_ON(!(hw->flags & IEEE80211_HW_CONNECTION_MONITOR)); ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work); } @@ -2261,6 +2265,8 @@ void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif, { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + trace_api_cqm_rssi_notify(sdata, rssi_event); + cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp); } EXPORT_SYMBOL(ieee80211_cqm_rssi_notify); diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 75a85978c3b3..eb86a5f6e645 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -247,6 +247,8 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) struct ieee80211_local *local = hw_to_local(hw); bool was_hw_scan; + trace_api_scan_completed(local, aborted); + mutex_lock(&local->scan_mtx); /* diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 4de987cbda1c..ff0eb948917b 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -957,6 +957,8 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw, { struct sta_info *sta = container_of(pubsta, struct sta_info, sta); + trace_api_sta_block_awake(sta->local, pubsta, block); + if (block) set_sta_flags(sta, WLAN_STA_PS_DRIVER); else diff --git a/net/mac80211/util.c b/net/mac80211/util.c index ad9009f717ed..2b75b4fb68f4 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -270,6 +270,8 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue, struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_sub_if_data *sdata; + trace_wake_queue(local, queue, reason); + if (WARN_ON(queue >= hw->queues)) return; @@ -312,6 +314,8 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue, struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_sub_if_data *sdata; + trace_stop_queue(local, queue, reason); + if (WARN_ON(queue >= hw->queues)) return; -- cgit v1.2.3 From 195e294d21e88af879da4f88db2ceeb4ec28a755 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Tue, 27 Apr 2010 12:47:40 +0300 Subject: mac80211: Determine dynamic PS timeout based on ps-qos network latency Determine the dynamic PS timeout based on the configured ps-qos network latency. For backwards wext compatibility, allow the dynamic PS timeout configured by the cfg80211 to overrule the automatically determined value. Signed-off-by: Juuso Oikarinen Signed-off-by: John W. Linville --- include/net/mac80211.h | 5 ++++- net/mac80211/cfg.c | 4 ++-- net/mac80211/main.c | 2 ++ net/mac80211/mlme.c | 21 +++++++++++++++++++++ 4 files changed, 29 insertions(+), 3 deletions(-) (limited to 'net/mac80211/main.c') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 7dc5a6790397..c26de6cb12f8 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -668,6 +668,9 @@ enum ieee80211_smps_mode { * @dynamic_ps_timeout: The dynamic powersave timeout (in ms), see the * powersave documentation below. This variable is valid only when * the CONF_PS flag is set. + * @dynamic_ps_forced_timeout: The dynamic powersave timeout (in ms) configured + * by cfg80211 (essentially, wext) If set, this value overrules the value + * chosen by mac80211 based on ps qos network latency. * * @power_level: requested transmit power (in dBm) * @@ -687,7 +690,7 @@ enum ieee80211_smps_mode { */ struct ieee80211_conf { u32 flags; - int power_level, dynamic_ps_timeout; + int power_level, dynamic_ps_timeout, dynamic_ps_forced_timeout; int max_sleep_period; u16 listen_interval; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index e13fb3a62239..b575a5066219 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1404,11 +1404,11 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, return -EOPNOTSUPP; if (enabled == sdata->u.mgd.powersave && - timeout == conf->dynamic_ps_timeout) + timeout == conf->dynamic_ps_forced_timeout) return 0; sdata->u.mgd.powersave = enabled; - conf->dynamic_ps_timeout = timeout; + conf->dynamic_ps_forced_timeout = timeout; /* no change, but if automatic follow powersave */ mutex_lock(&sdata->u.mgd.mtx); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 4afe851cf8dc..ebcca0eaf1dc 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -569,6 +569,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) local->hw.conf.listen_interval = local->hw.max_listen_interval; + local->hw.conf.dynamic_ps_forced_timeout = -1; + result = sta_info_start(local); if (result < 0) goto fail_sta_info; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index d811e3fa1d75..2d1a2bef366d 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -475,6 +475,7 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency) { struct ieee80211_sub_if_data *sdata, *found = NULL; int count = 0; + int timeout; if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) { local->ps_sdata = NULL; @@ -508,6 +509,26 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency) beaconint_us = ieee80211_tu_to_usec( found->vif.bss_conf.beacon_int); + timeout = local->hw.conf.dynamic_ps_forced_timeout; + if (timeout < 0) { + /* + * The 2 second value is there for compatibility until + * the PM_QOS_NETWORK_LATENCY is configured with real + * values. + */ + if (latency == 2000000000) + timeout = 100; + else if (latency <= 50000) + timeout = 300; + else if (latency <= 100000) + timeout = 100; + else if (latency <= 500000) + timeout = 50; + else + timeout = 0; + } + local->hw.conf.dynamic_ps_timeout = timeout; + if (beaconint_us > latency) { local->ps_sdata = NULL; } else { -- cgit v1.2.3 From be4a4b6a5d2f76393f545a2545fbaa1b65577e13 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 3 May 2010 08:49:48 +0200 Subject: mac80211: improve IBSS scanning When IBSS is fixed to a frequency, it can still scan to try to find the right BSSID. This makes sense if the BSSID isn't also fixed, but it need not scan all channels -- just one is sufficient. Make it do that by moving the scan setup code to ieee80211_request_internal_scan() and include a channel variable setting. Note that this can be further improved to start the IBSS right away if both frequency and BSSID are fixed. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/ibss.c | 9 ++++++--- net/mac80211/ieee80211_i.h | 3 ++- net/mac80211/main.c | 17 +---------------- net/mac80211/scan.c | 28 +++++++++++++++++++++++++++- 4 files changed, 36 insertions(+), 21 deletions(-) (limited to 'net/mac80211/main.c') diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index c585fced8584..ba752362b2b2 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -488,7 +488,9 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata) printk(KERN_DEBUG "%s: No active IBSS STAs - trying to scan for other " "IBSS networks with same SSID (merge)\n", sdata->name); - ieee80211_request_internal_scan(sdata, ifibss->ssid, ifibss->ssid_len); + ieee80211_request_internal_scan(sdata, + ifibss->ssid, ifibss->ssid_len, + ifibss->fixed_channel ? ifibss->channel : NULL); } static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata) @@ -595,8 +597,9 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) printk(KERN_DEBUG "%s: Trigger new scan to find an IBSS to " "join\n", sdata->name); - ieee80211_request_internal_scan(sdata, ifibss->ssid, - ifibss->ssid_len); + ieee80211_request_internal_scan(sdata, + ifibss->ssid, ifibss->ssid_len, + ifibss->fixed_channel ? ifibss->channel : NULL); } else { int interval = IEEE80211_SCAN_INTERVAL; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 4e73660ebe99..c8077a3647c6 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1020,7 +1020,8 @@ void ieee80211_ibss_restart(struct ieee80211_sub_if_data *sdata); /* scan/BSS handling */ void ieee80211_scan_work(struct work_struct *work); int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, - const u8 *ssid, u8 ssid_len); + const u8 *ssid, u8 ssid_len, + struct ieee80211_channel *chan); int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, struct cfg80211_scan_request *req); void ieee80211_scan_cancel(struct ieee80211_local *local); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index ebcca0eaf1dc..353b6b42d9c5 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -439,7 +439,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) struct ieee80211_local *local = hw_to_local(hw); int result; enum ieee80211_band band; - int channels, i, j, max_bitrates; + int channels, max_bitrates; bool supp_ht; static const u32 cipher_suites[] = { WLAN_CIPHER_SUITE_WEP40, @@ -605,21 +605,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) ieee80211_led_init(local); - /* alloc internal scan request */ - i = 0; - local->int_scan_req->ssids = &local->scan_ssid; - local->int_scan_req->n_ssids = 1; - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - if (!hw->wiphy->bands[band]) - continue; - for (j = 0; j < hw->wiphy->bands[band]->n_channels; j++) { - local->int_scan_req->channels[i] = - &hw->wiphy->bands[band]->channels[j]; - i++; - } - } - local->int_scan_req->n_channels = i; - local->network_latency_notifier.notifier_call = ieee80211_max_network_latency; result = pm_qos_add_notifier(PM_QOS_NETWORK_LATENCY, diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index a9d40584e383..414651217b49 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -728,10 +728,12 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, } int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, - const u8 *ssid, u8 ssid_len) + const u8 *ssid, u8 ssid_len, + struct ieee80211_channel *chan) { struct ieee80211_local *local = sdata->local; int ret = -EBUSY; + enum nl80211_band band; mutex_lock(&local->scan_mtx); @@ -739,6 +741,30 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, if (local->scan_req) goto unlock; + /* fill internal scan request */ + if (!chan) { + int i, nchan = 0; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (!local->hw.wiphy->bands[band]) + continue; + for (i = 0; + i < local->hw.wiphy->bands[band]->n_channels; + i++) { + local->int_scan_req->channels[nchan] = + &local->hw.wiphy->bands[band]->channels[i]; + nchan++; + } + } + + local->int_scan_req->n_channels = nchan; + } else { + local->int_scan_req->channels[0] = chan; + local->int_scan_req->n_channels = 1; + } + + local->int_scan_req->ssids = &local->scan_ssid; + local->int_scan_req->n_ssids = 1; memcpy(local->int_scan_req->ssids[0].ssid, ssid, IEEE80211_MAX_SSID_LEN); local->int_scan_req->ssids[0].ssid_len = ssid_len; -- cgit v1.2.3 From 0aaffa9b9699894aab3266195a529baf9f96ac29 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 5 May 2010 15:28:27 +0200 Subject: mac80211: improve HT channel handling Currently, when one interface switches HT mode, all others will follow along. This is clearly undesirable, since the new one might switch to no-HT while another one is operating in HT. Address this issue by keeping track of the HT mode per interface, and allowing only changes that are compatible, i.e. switching into HT40+ is not possible when another interface is in HT40-, in that case the second one needs to fall back to HT20. Also, to allow drivers to know what's going on, store the per-interface HT mode (channel type) in the virtual interface's bss_conf. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/mac80211_hwsim.c | 19 +++++----- include/net/mac80211.h | 4 ++ net/mac80211/cfg.c | 23 +++++++++--- net/mac80211/chan.c | 70 +++++++++++++++++++++++++++++++++++ net/mac80211/ibss.c | 5 ++- net/mac80211/ieee80211_i.h | 5 ++- net/mac80211/main.c | 2 +- net/mac80211/mlme.c | 44 +++++++++++----------- 8 files changed, 132 insertions(+), 40 deletions(-) (limited to 'net/mac80211/main.c') diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 62a739f6c37b..bdce71a4ba20 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -651,17 +651,17 @@ static void mac80211_hwsim_beacon(unsigned long arg) add_timer(&data->beacon_timer); } +static const char *hwsim_chantypes[] = { + [NL80211_CHAN_NO_HT] = "noht", + [NL80211_CHAN_HT20] = "ht20", + [NL80211_CHAN_HT40MINUS] = "ht40-", + [NL80211_CHAN_HT40PLUS] = "ht40+", +}; static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed) { struct mac80211_hwsim_data *data = hw->priv; struct ieee80211_conf *conf = &hw->conf; - static const char *chantypes[4] = { - [NL80211_CHAN_NO_HT] = "noht", - [NL80211_CHAN_HT20] = "ht20", - [NL80211_CHAN_HT40MINUS] = "ht40-", - [NL80211_CHAN_HT40PLUS] = "ht40+", - }; static const char *smps_modes[IEEE80211_SMPS_NUM_MODES] = { [IEEE80211_SMPS_AUTOMATIC] = "auto", [IEEE80211_SMPS_OFF] = "off", @@ -672,7 +672,7 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed) printk(KERN_DEBUG "%s:%s (freq=%d/%s idle=%d ps=%d smps=%s)\n", wiphy_name(hw->wiphy), __func__, conf->channel->center_freq, - chantypes[conf->channel_type], + hwsim_chantypes[conf->channel_type], !!(conf->flags & IEEE80211_CONF_IDLE), !!(conf->flags & IEEE80211_CONF_PS), smps_modes[conf->smps_mode]); @@ -760,9 +760,10 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw, } if (changed & BSS_CHANGED_HT) { - printk(KERN_DEBUG " %s: HT: op_mode=0x%x\n", + printk(KERN_DEBUG " %s: HT: op_mode=0x%x, chantype=%s\n", wiphy_name(hw->wiphy), - info->ht_operation_mode); + info->ht_operation_mode, + hwsim_chantypes[info->channel_type]); } if (changed & BSS_CHANGED_BASIC_RATES) { diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 0528615ac827..9448a5b1bb15 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -191,6 +191,9 @@ enum ieee80211_bss_change { * the current band. * @bssid: The BSSID for this BSS * @enable_beacon: whether beaconing should be enabled or not + * @channel_type: Channel type for this BSS -- the hardware might be + * configured for HT40+ while this BSS only uses no-HT, for + * example. * @ht_operation_mode: HT operation mode (like in &struct ieee80211_ht_info). * This field is only valid when the channel type is one of the HT types. * @cqm_rssi_thold: Connection quality monitor RSSI threshold, a zero value @@ -215,6 +218,7 @@ struct ieee80211_bss_conf { u16 ht_operation_mode; s32 cqm_rssi_thold; u32 cqm_rssi_hyst; + enum nl80211_channel_type channel_type; }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 414b7dd7d7fd..ab166c6d9399 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1166,23 +1166,34 @@ static int ieee80211_set_channel(struct wiphy *wiphy, enum nl80211_channel_type channel_type) { struct ieee80211_local *local = wiphy_priv(wiphy); + struct ieee80211_sub_if_data *sdata = NULL; + + if (netdev) + sdata = IEEE80211_DEV_TO_SUB_IF(netdev); switch (ieee80211_get_channel_mode(local, NULL)) { case CHAN_MODE_HOPPING: return -EBUSY; case CHAN_MODE_FIXED: - if (local->oper_channel == chan && - local->oper_channel_type == channel_type) + if (local->oper_channel != chan) + return -EBUSY; + if (!sdata && local->_oper_channel_type == channel_type) return 0; - return -EBUSY; + break; case CHAN_MODE_UNDEFINED: break; } local->oper_channel = chan; - local->oper_channel_type = channel_type; - return ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); + if (!ieee80211_set_channel_type(local, sdata, channel_type)) + return -EBUSY; + + ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); + if (sdata && sdata->vif.type != NL80211_IFTYPE_MONITOR) + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_HT); + + return 0; } #ifdef CONFIG_PM @@ -1406,7 +1417,7 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, * association, there's no need to send an action frame. */ if (!sdata->u.mgd.associated || - sdata->local->oper_channel_type == NL80211_CHAN_NO_HT) { + sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) { mutex_lock(&sdata->local->iflist_mtx); ieee80211_recalc_smps(sdata->local, sdata); mutex_unlock(&sdata->local->iflist_mtx); diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 08f3832661a5..5d218c530a4e 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -2,6 +2,7 @@ * mac80211 - channel management */ +#include #include "ieee80211_i.h" enum ieee80211_chan_mode @@ -55,3 +56,72 @@ ieee80211_get_channel_mode(struct ieee80211_local *local, return mode; } + +bool ieee80211_set_channel_type(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + enum nl80211_channel_type chantype) +{ + struct ieee80211_sub_if_data *tmp; + enum nl80211_channel_type superchan = NL80211_CHAN_NO_HT; + bool result; + + mutex_lock(&local->iflist_mtx); + + list_for_each_entry(tmp, &local->interfaces, list) { + if (tmp == sdata) + continue; + + if (!ieee80211_sdata_running(tmp)) + continue; + + switch (tmp->vif.bss_conf.channel_type) { + case NL80211_CHAN_NO_HT: + case NL80211_CHAN_HT20: + superchan = tmp->vif.bss_conf.channel_type; + break; + case NL80211_CHAN_HT40PLUS: + WARN_ON(superchan == NL80211_CHAN_HT40MINUS); + superchan = NL80211_CHAN_HT40PLUS; + break; + case NL80211_CHAN_HT40MINUS: + WARN_ON(superchan == NL80211_CHAN_HT40PLUS); + superchan = NL80211_CHAN_HT40MINUS; + break; + } + } + + switch (superchan) { + case NL80211_CHAN_NO_HT: + case NL80211_CHAN_HT20: + /* + * allow any change that doesn't go to no-HT + * (if it already is no-HT no change is needed) + */ + if (chantype == NL80211_CHAN_NO_HT) + break; + superchan = chantype; + break; + case NL80211_CHAN_HT40PLUS: + case NL80211_CHAN_HT40MINUS: + /* allow smaller bandwidth and same */ + if (chantype == NL80211_CHAN_NO_HT) + break; + if (chantype == NL80211_CHAN_HT20) + break; + if (superchan == chantype) + break; + result = false; + goto out; + } + + local->_oper_channel_type = superchan; + + if (sdata) + sdata->vif.bss_conf.channel_type = chantype; + + result = true; + out: + mutex_unlock(&local->iflist_mtx); + + return result; +} diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index d5855ae387e8..36745f494f63 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -102,7 +102,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0; local->oper_channel = chan; - local->oper_channel_type = NL80211_CHAN_NO_HT; + WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT)); ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); sband = local->hw.wiphy->bands[chan->band]; @@ -910,7 +910,8 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, /* fix ourselves to that channel now already */ if (params->channel_fixed) { sdata->local->oper_channel = params->channel; - sdata->local->oper_channel_type = NL80211_CHAN_NO_HT; + WARN_ON(!ieee80211_set_channel_type(sdata->local, sdata, + NL80211_CHAN_NO_HT)); } if (params->ie) { diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 359edff31471..69e7f4131f46 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -768,7 +768,7 @@ struct ieee80211_local { enum mac80211_scan_state next_scan_state; struct delayed_work scan_work; struct ieee80211_sub_if_data *scan_sdata; - enum nl80211_channel_type oper_channel_type; + enum nl80211_channel_type _oper_channel_type; struct ieee80211_channel *oper_channel, *csa_channel; /* Temporary remain-on-channel for off-channel operations */ @@ -1239,6 +1239,9 @@ enum ieee80211_chan_mode { enum ieee80211_chan_mode ieee80211_get_channel_mode(struct ieee80211_local *local, struct ieee80211_sub_if_data *ignore); +bool ieee80211_set_channel_type(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + enum nl80211_channel_type chantype); #ifdef CONFIG_MAC80211_NOINLINE #define debug_noinline noinline diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 353b6b42d9c5..d763d76e809f 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -111,7 +111,7 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) channel_type = local->tmp_channel_type; } else { chan = local->oper_channel; - channel_type = local->oper_channel_type; + channel_type = local->_oper_channel_type; } if (chan != local->hw.conf.channel || diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 17cb8ae912bc..6e149b49d4f0 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -136,11 +136,14 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, struct sta_info *sta; u32 changed = 0; u16 ht_opmode; - bool enable_ht = true, ht_changed; + bool enable_ht = true; + enum nl80211_channel_type prev_chantype; enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; + prev_chantype = sdata->vif.bss_conf.channel_type; + /* HT is not supported */ if (!sband->ht_cap.ht_supported) enable_ht = false; @@ -171,38 +174,37 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, } } - ht_changed = conf_is_ht(&local->hw.conf) != enable_ht || - channel_type != local->hw.conf.channel_type; - if (local->tmp_channel) local->tmp_channel_type = channel_type; - local->oper_channel_type = channel_type; - if (ht_changed) { - /* channel_type change automatically detected */ - ieee80211_hw_config(local, 0); + if (!ieee80211_set_channel_type(local, sdata, channel_type)) { + /* can only fail due to HT40+/- mismatch */ + channel_type = NL80211_CHAN_HT20; + WARN_ON(!ieee80211_set_channel_type(local, sdata, channel_type)); + } + /* channel_type change automatically detected */ + ieee80211_hw_config(local, 0); + + if (prev_chantype != channel_type) { rcu_read_lock(); sta = sta_info_get(sdata, bssid); if (sta) rate_control_rate_update(local, sband, sta, IEEE80211_RC_HT_CHANGED, - local->oper_channel_type); + channel_type); rcu_read_unlock(); - } - - /* disable HT */ - if (!enable_ht) - return 0; + } ht_opmode = le16_to_cpu(hti->operation_mode); /* if bss configuration changed store the new one */ - if (!sdata->ht_opmode_valid || - sdata->vif.bss_conf.ht_operation_mode != ht_opmode) { + if (sdata->ht_opmode_valid != enable_ht || + sdata->vif.bss_conf.ht_operation_mode != ht_opmode || + prev_chantype != channel_type) { changed |= BSS_CHANGED_HT; sdata->vif.bss_conf.ht_operation_mode = ht_opmode; - sdata->ht_opmode_valid = true; + sdata->ht_opmode_valid = enable_ht; } return changed; @@ -865,7 +867,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, ieee80211_set_wmm_default(sdata); /* channel(_type) changes are handled by ieee80211_hw_config */ - local->oper_channel_type = NL80211_CHAN_NO_HT; + WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT)); /* on the next assoc, re-program HT parameters */ sdata->ht_opmode_valid = false; @@ -882,8 +884,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, ieee80211_hw_config(local, config_changed); - /* And the BSSID changed -- not very interesting here */ - changed |= BSS_CHANGED_BSSID; + /* The BSSID (not really interesting) and HT changed */ + changed |= BSS_CHANGED_BSSID | BSS_CHANGED_HT; ieee80211_bss_info_change_notify(sdata, changed); if (remove_sta) @@ -2265,7 +2267,7 @@ int ieee80211_mgd_action(struct ieee80211_sub_if_data *sdata, if ((chan != local->tmp_channel || channel_type != local->tmp_channel_type) && (chan != local->oper_channel || - channel_type != local->oper_channel_type)) + channel_type != local->_oper_channel_type)) return -EBUSY; skb = dev_alloc_skb(local->hw.extra_tx_headroom + len); -- cgit v1.2.3