From: Johannes Berg <johannes.berg@xxxxxxxxx> Now that we can access the regulatory database without holding locks, check the desired bandwidth against it when a channel definition is checked. This addresses the missing VHT bandwidth check, HT is already handled by setting flags when the regdomain changes. To do this modify freq_reg_info() to be callable with RCU protection and use that, acquiring the mutex isn't possible for this function. Signed-off-by: Johannes Berg <johannes.berg@xxxxxxxxx> --- net/wireless/chan.c | 12 +++++++++++- net/wireless/reg.c | 39 +++++++++++++++++++++++++++++++-------- net/wireless/reg.h | 3 +++ 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 810c23c..9053345 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -10,6 +10,7 @@ #include <net/cfg80211.h> #include "core.h" #include "rdev-ops.h" +#include "reg.h" void cfg80211_chandef_create(struct cfg80211_chan_def *chandef, struct ieee80211_channel *chan, @@ -327,6 +328,9 @@ static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, /* check for the other flags */ if (c->flags & prohibited_flags & ~IEEE80211_CHAN_RADAR) return false; + if (!reg_check_bandwidth(wiphy, MHZ_TO_KHZ(freq), + MHZ_TO_KHZ(bandwidth))) + return false; } return true; @@ -389,7 +393,13 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy, return false; } - /* TODO: missing regulatory check on 80/160 bandwidth */ + /* + * TODO: What if there are only certain 80/160/80+80 MHz channels + * allowed by the driver, or only certain combinations? + * For 40 MHz the driver can set the NO_HT40 flags, but for + * 80/160 MHz and in particular 80+80 MHz this isn't really + * feasible -- should we ask the driver here? + */ if (width > 20) prohibited_flags |= IEEE80211_CHAN_NO_OFDM; diff --git a/net/wireless/reg.c b/net/wireless/reg.c index bf35e1f..343b390 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -121,14 +121,14 @@ static inline void assert_reg_lock(void) static const struct ieee80211_regdomain *get_cfg80211_regdom(void) { - return rcu_dereference_protected(cfg80211_regdomain, - lockdep_is_held(®_mutex)); + return rcu_dereference_check(cfg80211_regdomain, + lockdep_is_held(®_mutex)); } static const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy) { - return rcu_dereference_protected(wiphy->regd, - lockdep_is_held(®_mutex)); + return rcu_dereference_check(wiphy->regd, + lockdep_is_held(®_mutex)); } static void rcu_free_regdom(const struct ieee80211_regdomain *r) @@ -751,7 +751,7 @@ freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq, const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy, u32 center_freq) { - const struct ieee80211_regdomain *regd; + const struct ieee80211_regdomain *regd = NULL; struct regulatory_request *lr = get_last_request(); /* @@ -759,16 +759,39 @@ const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy, * IE has been processed or a user wants to help complaince further */ if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && - lr->initiator != NL80211_REGDOM_SET_BY_USER && - wiphy->regd) + lr->initiator != NL80211_REGDOM_SET_BY_USER) regd = get_wiphy_regdom(wiphy); - else + + if (!regd) regd = get_cfg80211_regdom(); return freq_reg_info_regd(wiphy, center_freq, regd); } EXPORT_SYMBOL(freq_reg_info); +bool reg_check_bandwidth(struct wiphy *wiphy, + u32 center_freq_khz, u32 bw_khz) +{ + const struct ieee80211_reg_rule *reg_rule; + bool result = false; + + /* + * This interpretation is a bit of a strange quirk in the regulatory + * rules definitions that we have today: each 20 MHz channel must fit + * entirely into a single regulatory range, but if this range forbids + * using more than 20 MHz then it forbids even using a small part of + * this for the wider channel. + */ + + rcu_read_lock(); + reg_rule = freq_reg_info(wiphy, center_freq_khz); + if (!IS_ERR(reg_rule)) + result = reg_rule->freq_range.max_bandwidth_khz >= bw_khz; + rcu_read_unlock(); + + return result; +} + #ifdef CONFIG_CFG80211_REG_DEBUG static const char *reg_initiator_name(enum nl80211_reg_initiator initiator) { diff --git a/net/wireless/reg.h b/net/wireless/reg.h index af2d5f8..d6740ab 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -35,6 +35,9 @@ int set_regdom(const struct ieee80211_regdomain *rd); bool reg_last_request_cell_base(void); +bool reg_check_bandwidth(struct wiphy *wiphy, + u32 center_freq_khz, u32 bw_khz); + /** * regulatory_hint_found_beacon - hints a beacon was found on a channel * @wiphy: the wireless device where the beacon was found on -- 1.8.0 -- To unsubscribe from this list: send the line "unsubscribe linux-wireless" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html