Add support for ACS offload for AP
cover Add support for ACS offload for AP
The following set of patches add support for Automatic Channel Selection (ACS) offload for AP in cfg80211 and an implementation in wil6210 driver.
Ahmad Masri (2):
cfg80211: Add Automatic Channel Selection (ACS) offload for AP
wil6210: add implementation of acs cfg80211_ops
drivers/net/wireless/ath/wil6210/cfg80211.c | 140 ++++++++++++++++++++- drivers/net/wireless/ath/wil6210/debugfs.c | 154 +++++++++++++++++++++++ drivers/net/wireless/ath/wil6210/main.c | 5 + drivers/net/wireless/ath/wil6210/wil6210.h | 15 +++ drivers/net/wireless/ath/wil6210/wmi.c | 76 ++++++++++++ drivers/net/wireless/ath/wil6210/wmi.h | 3 +- include/net/cfg80211.h | 33 +++++ include/uapi/linux/nl80211.h | 65 ++++++++++ net/wireless/mlme.c | 31 +++++ net/wireless/nl80211.c | 182 ++++++++++++++++++++++++++++ net/wireless/nl80211.h | 6 + net/wireless/rdev-ops.h | 12 ++ net/wireless/trace.h | 23 ++++ 13 files changed, 742 insertions(+), 3 deletions(-)
Signature · 1 line
1.9.1
PATCH 1/2 cfg80211: Add Automatic Channel Selection (ACS) offload for AP
Add ACS offload to let the device select a channel for AP from a list of channels that the user-space provides. ACS can customize the method of AP channel selection and add parameters like traffic load, number of APs on channel and more.
--- include/net/cfg80211.h | 33 ++++++++ include/uapi/linux/nl80211.h | 65 ++++++++++++++++ net/wireless/mlme.c | 31 ++++++++ net/wireless/nl80211.c | 182 +++++++++++++++++++++++++++++++++++++++++++ net/wireless/nl80211.h | 6 ++ net/wireless/rdev-ops.h | 12 +++ net/wireless/trace.h | 23 ++++++ 7 files changed, 352 insertions(+)
include/net/cfg80211.h +33 -0
@@ -715,6 +715,17 @@ struct survey_info {s8 noise; }; +/** +* struct cfg80211_acs_params - ACS parameters +* @n_channels: total number of channels for ACS +* @channels: list of chan_def to run ACS on +*/ +struct cfg80211_acs_params { +u32 n_channels; +/* keep last */ +struct cfg80211_chan_def channels[0 ]; +}; +#define CFG80211_MAX_WEP_KEYS 4/**
@@ -3377,6 +3388,11 @@ struct cfg80211_pmsr_request {* Statistics should be cumulative, currently no way to reset is provided. * @start_pmsr: start peer measurement (e.g. FTM) * @abort_pmsr: abort peer measurement +* @acs: run automatic channel selection offload measurement to find the best +* channel to start the AP on. Userspace provide a list of chan_defs, the +* driver should perform ACS on the provided list and select best channel. +* driver should call cfg80211_acs_result to complete this operation. +* (invoked with the wireless_dev mutex held) */ struct cfg80211_ops {int (*suspend)(struct wiphy *wiphy,struct cfg80211_wowlan *wow);
@@ -3691,6 +3707,8 @@ struct cfg80211_ops { struct cfg80211_pmsr_request *request);void (*abort_pmsr)(struct wiphy *wiphy,struct wireless_dev *wdev,struct cfg80211_pmsr_request *request); +int (*acs)(struct wiphy *wiphy,struct net_device *dev, +struct cfg80211_acs_params *params); };/*
@@ -4662,6 +4680,7 @@ struct wireless_dev {unsigned long cac_start_time;unsigned int cac_time_ms; +bool acs_started; #ifdef CONFIG_CFG80211_WEXT /* wext data */ struct {
@@ -6362,6 +6381,20 @@ void cfg80211_cac_event(struct net_device *netdev,/** +* cfg80211_acs_result - ACS result notification +* @netdev: network device +* @chandef: chandef for the selected channel, NULL on unsuccessful operation +* @status: ACS status as specified in &enum nl80211_acs_status +* @gfp: context flags +* +* This function is called when ACS measurement is finished or aborted. +* This must be called to notify the completion of a ACS process. +*/ +void cfg80211_acs_result (struct net_device *netdev, +const struct cfg80211_chan_def *chandef, +enum nl80211_acs_status status,gfp_t gfp); + +/** * cfg80211_gtk_rekey_notify - notify userspace about driver rekeying * @dev: network device * @bssid: BSSID of AP (to avoid races)
include/uapi/linux/nl80211.h +65 -0
113 lines not on this page · raw message
net/wireless/mlme.c +31 -0
@@ -903,3 +903,34 @@ void cfg80211_cac_event(struct net_device *netdev,nl80211_radar_notify (rdev, chandef, event, netdev, gfp); }EXPORT_SYMBOL (cfg80211_cac_event); + +void cfg80211_acs_result (struct net_device *netdev, +const struct cfg80211_chan_def *chandef, +enum nl80211_acs_status status,gfp_t gfp) +{ +struct wireless_dev *wdev = netdev->ieee80211_ptr; +struct wiphy *wiphy = wdev->wiphy; +struct cfg80211_registered_device *rdev =wiphy_to_rdev (wiphy); + +if (WARN_ON (!wdev->acs_started)) +return ; + +switch (status) { +case NL80211_ACS_SUCCESS : +if (!chandef) { +WARN_ON (1 ); + goto out; + } +case NL80211_ACS_FAILED : +break ; +default : +WARN_ON (1 ); + goto out; + } +nl80211_acs_notify (rdev, chandef, status, netdev, gfp); + +out: + wdev->acs_started = false; +dev_put (netdev); +} +EXPORT_SYMBOL (cfg80211_acs_result);
net/wireless/nl80211.c +182 -0
218 lines not on this page · raw message
net/wireless/nl80211.h +6 -0
@@ -119,6 +119,12 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,enum nl80211_radar_event event, struct net_device *netdev, gfp_t gfp); +void +nl80211_acs_notify (struct cfg80211_registered_device *rdev, +const struct cfg80211_chan_def *chandef, +enum nl80211_acs_status status, +struct net_device *netdev,gfp_t gfp); +void nl80211_send_ap_stopped (struct wireless_dev *wdev);void cfg80211_rdev_free_coalesce (struct cfg80211_registered_device *rdev);
net/wireless/rdev-ops.h +12 -0
@@ -1272,4 +1272,16 @@ static inline int rdev_del_pmk(struct cfg80211_registered_device *rdev, trace_rdev_return_void(&rdev->wiphy); } +static inline int rdev_acs (struct cfg80211_registered_device *rdev, +struct net_device *dev, +struct cfg80211_acs_params *params) +{ +int ret; + +trace_rdev_acs (&rdev->wiphy, dev, params); + ret = rdev->ops->acs (&rdev->wiphy, dev, params); +trace_rdev_return_int (&rdev->wiphy, ret); +return ret; +} +#endif /* __CFG80211_RDEV_OPS */
net/wireless/trace.h +23 -0
@@ -2530,6 +2530,29 @@TP_ARGS (wiphy, wdev, cookie) ); +TRACE_EVENT (rdev_acs, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_acs_params *params), + +TP_ARGS (wiphy, netdev, params), + +TP_STRUCT__entry ( + WIPHY_ENTRY + NETDEV_ENTRY +__field (u32, n_channels) + ), + +TP_fast_assign ( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + __entry->n_channels = params->n_channels; + ), + +TP_printk (WIPHY_PR_FMT ", " NETDEV_PR_FMT +", num of channels: %u" ,WIPHY_PR_ARG ,NETDEV_PR_ARG , + __entry->n_channels) +); +/************************************************************* * cfg80211 exported functions traces * *************************************************************/
Signature · 1 line
1.9.1
Re: [PATCH 1/2] cfg80211: Add Automatic Channel Selection (ACS) offload for AP
Quoted 7 lines · Ahmad Masri
Add ACS offload to let the device select a channel for AP from a list of channels that the user-space provides. ACS can customize the method of AP channel selection and add parameters like traffic load, number of APs on channel and more.
Change-Id I18cb8460b9418512ac7ac9f76fe8f7f379f2478bSigned-off-by Ahmad Masri <[email protected]>
No Change-Id, please.
include/net/cfg80211.h struct wireless_dev { · line 4684
Quoted 6 lines
include/net/cfg80211.h +1
-0@@ -4662,6 +4680,7 @@ struct wireless_dev {unsigned long cac_start_time;unsigned int cac_time_ms; +bool acs_started;#ifdef CONFIG_CFG80211_WEXT
Empty line before ifdef? Just like it had before your patch.
Quoted 7 lines
net/wireless/mlme.c +7
-0+void cfg80211_acs_result (struct net_device *netdev, +const struct cfg80211_chan_def *chandef, +enum nl80211_acs_status status,gfp_t gfp) +{ +struct wireless_dev *wdev = netdev->ieee80211_ptr; +struct wiphy *wiphy = wdev->wiphy; +struct cfg80211_registered_device *rdev =wiphy_to_rdev (wiphy);
net/wireless/mlme.c void cfg80211_cac_event(struct net_device *netdev, · line 923
Quoted 10 lines
net/wireless/mlme.c +10
-0+ +if (WARN_ON (!wdev->acs_started)) +return ; + +switch (status) { +case NL80211_ACS_SUCCESS : +if (!chandef) { +WARN_ON (1 ); + goto out; + }
if (WARN_ON(!chandef))
goto out;
Signature · 1 line
Kalle Valo
Johannes Berg
maintainerRe: [PATCH 1/2] cfg80211: Add Automatic Channel Selection (ACS) offload for AP
In reply to Ahmad MasriSetting aside Kalle's question for a second (though you should address them, probably before mine!)...
Quoted 5 lines
+ * @NL80211_ATTR_CHAN_DEF: attribute for nesting
chan_defparameters
+ * as defined in &enumnl80211_ch_def_attr. The new attribute allows
+ * userspace to send a list of structcfg80211_chan_def. For example, ACS
+ * command uses this attribute for sending a list of channels, for more
+ * details see&NL80211_CMD_ACS.
I don't think this makes sense. Just have an NL80211_ATTR_CHANNEL_LIST or so, which is like a NLA_POLICY_NESTED_ARRAY, and then inside we can reuse the existing top-level attributes for how to define a channel.
That'll save you most of the parsing code too.
Quoted 10 lines
/**
+ * enumnl80211_acs_status- ACS status
+ *
+ * status to be used to inform userspace about the result of the ACS offloaded
+ * measurement.
+ *
+ * @NL80211_ACS_SUCCESS: The ACS operation finished successfully
+ * @NL80211_ACS_FAILED: Failed to run the ACS. Userspace should choose a channel
+ * by itself.
+ */
include/uapi/linux/nl80211.h enum nl80211_dfs_state { · line 5626
Quoted 4 lines
include/uapi/linux/nl80211.h +4
-0+enum nl80211_acs_status { +NL80211_ACS_SUCCESS , +NL80211_ACS_FAILED , +};
It seems like a "successful" flag attribute would be sufficient.
net/wireless/nl80211.c static int nl80211_get_ftm_responder_stats(struct sk_buff *skb, · line 13264
Quoted 3 lines
net/wireless/nl80211.c +2
-0+dev_put (netdev); +dev_hold (dev);
Don't do that. Need to cancel the operation if the netdev is removed instead.
johannes
PATCH 2/2 wil6210: add implementation of acs cfg80211_ops
In reply to Ahmad MasriAdd ACS to cfg80211 operations that the device handles and performs. wil6210 reports NL80211_EXT_FEATURE_ACS_OFFLOAD indicating a support for acs operation through cfg80211_ops.
ACS is performed by the driver and the FW. Once ACS operation is called the driver builds a WMI command with the requested channels and sends the command to the FW. The FW performs ACS scan on the channels and reports for each channel the following:
1- Number of beacon received on channel
2- channel busy time
3- transmit time
4- receive time
5- noise level
The driver uses the above information to select the best channel to start the AP, and reports it to the host.
User may use debugfs acs_ch_weight to set a different weights for the channels.
--- drivers/net/wireless/ath/wil6210/cfg80211.c | 140 ++++++++++++++++++++++++- drivers/net/wireless/ath/wil6210/debugfs.c | 154 ++++++++++++++++++++++++++++ drivers/net/wireless/ath/wil6210/main.c | 5 + drivers/net/wireless/ath/wil6210/wil6210.h | 15 +++ drivers/net/wireless/ath/wil6210/wmi.c | 76 ++++++++++++++ drivers/net/wireless/ath/wil6210/wmi.h | 3 +- 6 files changed, 390 insertions(+), 3 deletions(-)
drivers/net/wireless/ath/wil6210/cfg80211.c +138 -2
@@ -35,6 +35,14 @@ };#endif +/* in case of channels' noise values all zero, applying weights will not work. +* to avoid such a case, we will add some small positive value to +* all channels' noise calculation +*/ +#define ACS_CH_NOISE_INIT_VAL (100) + +#define ACS_DEFAULT_BEST_CHANNEL 2 +#define CHAN60G (_channel, _flags) { \ .band = NL80211_BAND_60GHZ, \ .center_freq = 56160 + (2160 * (_channel)), \
@@ -44,7 +52,7 @@ .max_power = 40, \ } -static struct ieee80211_channel wil_60ghz_channels[] = { +static struct ieee80211_channel wil_60ghz_channels[WIL_MAX_CHANNELS ] = {CHAN60G (1 ,0 ),CHAN60G (2 ,0 ),CHAN60G (3 ,0 ),
@@ -64,7 +72,7 @@ } } -static int wil_num_supported_channels (struct wil6210_priv *wil) +int wil_num_supported_channels (struct wil6210_priv *wil) {int num_channels =ARRAY_SIZE (wil_60ghz_channels);
@@ -2358,6 +2366,132 @@ static int wil_cfg80211_resume(struct wiphy *wiphy)return rc; } +static u8 wil_acs_calc_channel (struct wil6210_priv *wil) +{ +int i, best_channel =ACS_DEFAULT_BEST_CHANNEL -1 ; +struct scan_acs_info *ch; +u64 dwell_time =le32_to_cpu (wil->survey_reply.evt.dwell_time); +u16 filled =le16_to_cpu (wil->survey_reply.evt.filled); +u8 num_channels = wil->survey_reply.evt.num_scanned_channels; +u64 busy_time, tx_time; +u64 min_i_ch = (u64)-1 , cur_i_ch; +u8 p_min =0 , ch_noise; + +wil_dbg_misc (wil, +"acs_calc_channel: filled info: 0x%04X, for %u channels\n" , + filled, num_channels); + +if (!num_channels) { +wil_err (wil,"received results with no channel info\n" ); +return 0 ; + } + +/* find P_min */ +if (filled &WMI_ACS_INFO_BITMASK_NOISE ) { + p_min = wil->survey_reply.ch_info[0 ].noise; + +for (i =1 ; i < num_channels; i++) + p_min =min (p_min, wil->survey_reply.ch_info[i].noise); + } + +wil_dbg_misc (wil,"acs_calc_channel: p_min is %u\n" , p_min); + +/* Choosing channel according to the following formula: +* 16 bit fixed point math +* I_ch = { [ (T_busy - T_tx) << 16 ] / +* (T_dwell - T_tx) } * 2^(P_rx - P_min) +*/ +for (i =0 ; i < num_channels; i++) { + ch = &wil->survey_reply.ch_info[i]; + + busy_time = filled &WMI_ACS_INFO_BITMASK_BUSY_TIME ? +le16_to_cpu (ch->busy_time) :0 ; + + tx_time = filled &WMI_ACS_INFO_BITMASK_TX_TIME ? +le16_to_cpu (ch->tx_time) :0 ; + + ch_noise = filled &WMI_ACS_INFO_BITMASK_NOISE ? ch->noise :0 ; + +wil_dbg_misc (wil, +"acs_calc_channel: Ch[%d]: busy %llu, tx %llu, noise %u, dwell %llu\n" , + ch->channel +1 , busy_time, tx_time, ch_noise, + dwell_time); + +if (dwell_time == tx_time) { +wil_err (wil, +"Ch[%d] dwell_time == tx_time: %llu\n" , + ch->channel +1 , dwell_time); +continue ; + } + + cur_i_ch = (busy_time - tx_time) <<16 ; +do_div (cur_i_ch, + ((dwell_time - tx_time) << (ch_noise - p_min))); + +/* Apply channel priority */ + cur_i_ch = (cur_i_ch +ACS_CH_NOISE_INIT_VAL ) * + wil->acs_ch_weight[ch->channel]; +do_div (cur_i_ch,100 ); + +wil_dbg_misc (wil,"acs_calc_channel: Ch[%d] w %u, I_ch %llu\n" , + ch->channel +1 , wil->acs_ch_weight[ch->channel], + cur_i_ch); + +if (i ==0 || cur_i_ch < min_i_ch) { + min_i_ch = cur_i_ch; + best_channel = ch->channel; + } + } + +wil_dbg_misc (wil, +"acs_calc_channel: best channel %d with I_ch of %llu\n" , + best_channel +1 , min_i_ch); + +return best_channel; +} + +static void wil_acs_notify (struct wiphy *wiphy,struct net_device *dev, +u32 status) +{ +struct wil6210_priv *wil =wiphy_to_wil (wiphy); +u8 ch =wil_acs_calc_channel (wil); +u32 freq =ieee80211_channel_to_frequency (ch +1 ,NL80211_BAND_60GHZ ); +struct ieee80211_channel *channel =ieee80211_get_channel (wiphy, freq); +struct cfg80211_chan_def chandef = {0 }; + +if (channel) { +cfg80211_chandef_create (&chandef, channel,NL80211_CHAN_NO_HT ); + }else { +wil_err (wil,"Invalid freq %d\n" , freq); + status =NL80211_ACS_FAILED ; + } + +cfg80211_acs_result (dev, &chandef, status,GFP_KERNEL ); +} + +static int +wil_cfg80211_acs (struct wiphy *wiphy,struct net_device *dev, +struct cfg80211_acs_params *params) +{ +struct wil6210_priv *wil =wiphy_to_wil (wiphy); +enum nl80211_acs_status status =NL80211_ACS_SUCCESS ; +int rc; + +if (params->n_channels ==0 ) { +wil_err (wil,"acs: No valid channels for ACS\n" ); +return -EINVAL ; + } + + rc =wil_start_acs_survey (wil,WMI_SCAN_DWELL_TIME_MS , + params->channels, params->n_channels); +if (rc) + status =NL80211_ACS_FAILED ; + +wil_acs_notify (wiphy, dev, status); + +return 0 ; +} +static const struct cfg80211_ops wil_cfg80211_ops = { .add_virtual_intf = wil_cfg80211_add_iface, .del_virtual_intf = wil_cfg80211_del_iface,
@@ -2394,6 +2528,7 @@ static int wil_cfg80211_resume(struct wiphy *wiphy) .sched_scan_start = wil_cfg80211_sched_scan_start, .sched_scan_stop = wil_cfg80211_sched_scan_stop, .update_ft_ies = wil_cfg80211_update_ft_ies, + .acs = wil_cfg80211_acs, };static void wil_wiphy_init (struct wiphy *wiphy)
@@ -2436,6 +2571,7 @@ static void wil_wiphy_init(struct wiphy *wiphy)#ifdef CONFIG_PM wiphy->wowlan = &wil_wowlan_support;#endif +wiphy_ext_feature_set (wiphy,NL80211_EXT_FEATURE_ACS_OFFLOAD ); } int wil_cfg80211_iface_combinations_from_fw(
drivers/net/wireless/ath/wil6210/debugfs.c +154 -0
178 lines not on this page · raw message
drivers/net/wireless/ath/wil6210/main.c +5 -0
@@ -750,6 +750,11 @@ int wil_priv_init(struct wil6210_priv *wil) wil->amsdu_en =1 ; +/* ACS related */ + wil->acs_ch_weight[0 ] =120 ; +for (i =1 ; i <WIL_MAX_CHANNELS ; i++) + wil->acs_ch_weight[i] =100 ; +return 0 ; out_wmi_wq:
drivers/net/wireless/ath/wil6210/wil6210.h +15 -0
@@ -67,6 +67,8 @@*/ #define WIL_MAX_VIFS 4 +#define WIL_MAX_CHANNELS 4/* max supported channels */ +/** * extract bits [@b0:@b1] (inclusive) from the value @x * it should be @b0 <= @b1, or result is incorrect
@@ -1045,6 +1047,15 @@ struct wil6210_priv {u32 max_agg_wsize;u32 max_ampdu_size; + +/* ACS related */ +unsigned short acs_ch_weight[WIL_MAX_CHANNELS ]; +bool survey_ready; +struct { +struct wmi_cmd_hdr wmi; +struct wmi_acs_passive_scan_complete_event evt; +struct scan_acs_info ch_info[WIL_MAX_CHANNELS ]; + } __packed survey_reply; };#define wil_to_wiphy (i) (i->wiphy)
@@ -1252,6 +1263,9 @@ int wil_addba_rx_request(struct wil6210_priv *wil, u8 mid, u8 cidxtid, u8 dialog_token, __le16 ba_param_set, __le16 ba_timeout, __le16 ba_seq_ctrl);int wil_addba_tx_request (struct wil6210_priv *wil,u8 ringid,u16 wsize); +int wil_start_acs_survey (struct wil6210_priv *wil,uint dwell_time, +struct cfg80211_chan_def channels[], +u8 num_channels);void wil6210_clear_irq (struct wil6210_priv *wil);int wil6210_init_irq (struct wil6210_priv *wil,int irq);
@@ -1402,4 +1416,5 @@ int wmi_addba_rx_resp_edma(struct wil6210_priv *wil, u8 mid, u8 cid,void update_supported_bands (struct wil6210_priv *wil); +int wil_num_supported_channels (struct wil6210_priv *wil);#endif /* __WIL6210_H__ */
drivers/net/wireless/ath/wil6210/wmi.c +76 -0
79 lines not on this page · raw message
drivers/net/wireless/ath/wil6210/wmi.h +2 -1
@@ -2444,7 +2444,8 @@ struct wmi_acs_passive_scan_complete_event {*/ __le16 filled;u8 num_scanned_channels; -u8 reserved; +/* enum scan_status */ +u8 status;struct scan_acs_info scan_info_list[0 ]; } __packed;
Signature · 1 line
1.9.1
Re: [PATCH 2/2] wil6210: add implementation of acs cfg80211_ops
Quoted 4 lines · Ahmad Masri
Add ACS to cfg80211 operations that the device handles and performs. wil6210 reports
NL80211_EXT_FEATURE_ACS_OFFLOADindicating a support for acs operation throughcfg80211_ops.
Quoted 11 lines
ACS is performed by the driver and the FW. Once ACS operation is called the driver builds a WMI command with the requested channels and sends the command to the FW. The FW performs ACS scan on the channels and reports for each channel the following:
1- Number of beacon received on channel
2- channel busy time
3- transmit time
4- receive time
5- noise level
The driver uses the above information to select the best channel to start the AP, and reports it to the host.
What's the point? Why is it better that wil6210 makes the decision instead of hostapd?
Quoted 2 lines
User may use debugfs
acs_ch_weightto set a different weights for the channels.
Debugfs does not sound like the best interface for that, shouldn't it go via nl80211?
Quoted 1 line
Change-Id: I8e13296fad0c9fa8b15788fd37c23d825d41d8e5
No Change-Id, please.
Signature · 1 line
Kalle Valo