From patchwork Sun Nov 23 15:02:20 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arik Nemtsov X-Patchwork-Id: 5361741 Return-Path: X-Original-To: patchwork-linux-wireless@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 5F7369F2F5 for ; Sun, 23 Nov 2014 15:02:35 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id F2A8420256 for ; Sun, 23 Nov 2014 15:02:33 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 7064A20251 for ; Sun, 23 Nov 2014 15:02:32 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751467AbaKWPCa (ORCPT ); Sun, 23 Nov 2014 10:02:30 -0500 Received: from mail-wi0-f181.google.com ([209.85.212.181]:34765 "EHLO mail-wi0-f181.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751400AbaKWPC2 (ORCPT ); Sun, 23 Nov 2014 10:02:28 -0500 Received: by mail-wi0-f181.google.com with SMTP id r20so3459606wiv.8 for ; Sun, 23 Nov 2014 07:02:26 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=UDg+jlngpSIJgYP9p44Y58qSowZAoiDmgE4Pg8melh0=; b=KpE3nhZSg0Fb7Nu/fHr6hzFbms8Rd8aOSjMQpsXZXV0eDz3OsdCRLCgwbICeVrNqlb sjojPKWPp+bT/jBmT27d9oTjBGwDrxtn2JdejO0WEBLWTbEvlWWWlPe/OYFzKKU0cw/o xD9Mk6+L0oo7eKhkuIO0E21a5YX417AWPLAmmuLtvMnKjTd2zmdtFxofUv7xP3oMb1VH FXMFXYsJcABIo9kQEfGDlWD3QrLDP5LPG09weeohrUvk3UcKVJu6APFVxzkL0voCfElc ZTBRfaTBdIROnG5Br2FKqhY81sib5ETzmn573YHQOEaKH1KerOA3GsGR4cP8abWPEySt kPqQ== X-Gm-Message-State: ALoCoQkUL7VPfZzExD7RJihpYhDlWyRBA3UAciNM4kBM49/dkoSjjxl6nwxQ2WpmLCCbm0OlhNhB X-Received: by 10.194.249.232 with SMTP id yx8mr26241247wjc.1.1416754946850; Sun, 23 Nov 2014 07:02:26 -0800 (PST) Received: from cube.amr.corp.intel.com (85-250-108-142.bb.netvision.net.il. [85.250.108.142]) by mx.google.com with ESMTPSA id gs9sm16651444wjc.47.2014.11.23.07.02.25 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sun, 23 Nov 2014 07:02:26 -0800 (PST) From: Arik Nemtsov To: Cc: Johannes Berg , "Luis R. Rodriguez" , Jonathan Doron Subject: [PATCH v3 2/3] cfg80211: allow wiphy specific regdomain management Date: Sun, 23 Nov 2014 17:02:20 +0200 Message-Id: <1416754941-4439-2-git-send-email-arik@wizery.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1416754941-4439-1-git-send-email-arik@wizery.com> References: <1416754941-4439-1-git-send-email-arik@wizery.com> Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Jonathan Doron Add a new regulatory flag that allows a driver to manage regdomain changes/updates for its own wiphy. A self-managed wiphys only employs regulatory information obtained from the FW and driver and does not use other cfg80211 sources like beacon-hints, country-code IEs and hints from other devices on the same system. Conversely, a self-managed wiphy does not share its regulatory hints with other devices in the system. If a system contains several devices, one or more of which are self-managed, there might be contradictory regulatory settings between them. Usage of flag is generally discouraged. Only use it if the FW/driver is incompatible with non-locally originated hints. A new API lets the driver send a complete regdomain, to be applied on its wiphy only. After a wiphy-specific regdomain change takes place, usermode will get a new type of change notification. The regulatory core also takes care enforce regulatory restrictions, in case some interfaces are on forbidden channels. Signed-off-by: Jonathan Doron Signed-off-by: Arik Nemtsov Reviewed-by: Luis R. Rodriguez --- include/net/cfg80211.h | 16 +++++++++ include/net/regulatory.h | 19 +++++++++++ include/uapi/linux/nl80211.h | 5 +++ net/wireless/core.c | 8 +++++ net/wireless/nl80211.c | 80 ++++++++++++++++++++++++++++++++++---------- net/wireless/nl80211.h | 1 + net/wireless/reg.c | 59 ++++++++++++++++++++++++++++++++ 7 files changed, 170 insertions(+), 18 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index bb748c4..bfe630f 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3808,6 +3808,22 @@ const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type, int regulatory_hint(struct wiphy *wiphy, const char *alpha2); /** + * regulatory_set_wiphy_regd_rtnl - set regdom info for self managed drivers + * @wiphy: the wireless device we want to process the regulatory domain on + * @rd: the regulatory domain informatoin to use for this wiphy + * + * Set the regulatory domain information for self-managed wiphys, only they + * may use this function. See %REGULATORY_WIPHY_SELF_MANAGED for more + * information. + * + * This function requires the caller to hold the rtnl_lock. + * + * Return: 0 on success. -EINVAL, -EPERM + */ +int regulatory_set_wiphy_regd_rtnl(struct wiphy *wiphy, + struct ieee80211_regdomain *rd); + +/** * wiphy_apply_custom_regulatory - apply a custom driver regulatory domain * @wiphy: the wireless device we want to process the regulatory domain on * @regd: the custom regulatory domain to use for this wiphy diff --git a/include/net/regulatory.h b/include/net/regulatory.h index 701177c..d69c3db 100644 --- a/include/net/regulatory.h +++ b/include/net/regulatory.h @@ -141,6 +141,24 @@ struct regulatory_request { * change, the interfaces are given a grace period to disconnect or move * to an allowed channels. Interfaces on forbidden channels are forcibly * disconnected. + * @REGULATORY_WIPHY_SELF_MANAGED: for devices that employ wiphy-specific + * regdom management. These devices will ignore all regdom changes not + * originating from their own wiphy. + * A self-managed wiphys only employs regulatory information obtained from + * the FW and driver and does not use other cfg80211 sources like + * beacon-hints, country-code IEs and hints from other devices on the same + * system. Conversely, a self-managed wiphy does not share its regulatory + * hints with other devices in the system. If a system contains several + * devices, one or more of which are self-managed, there might be + * contradictory regulatory settings between them. Usage of flag is + * generally discouraged. Only use it if the FW/driver is incompatible + * with non-locally originated hints. + * This flag is incompatible with the flags: %REGULATORY_CUSTOM_REG, + * %REGULATORY_STRICT_REG, %REGULATORY_COUNTRY_IE_FOLLOW_POWER, + * %REGULATORY_COUNTRY_IE_IGNORE and %REGULATORY_DISABLE_BEACON_HINTS. + * Mixing any of the above flags with this flag will result in a failure + * to register the wiphy. This flag implies + * %REGULATORY_DISABLE_BEACON_HINTS. */ enum ieee80211_regulatory_flags { REGULATORY_CUSTOM_REG = BIT(0), @@ -150,6 +168,7 @@ enum ieee80211_regulatory_flags { REGULATORY_COUNTRY_IE_IGNORE = BIT(4), REGULATORY_ENABLE_RELAX_NO_IR = BIT(5), REGULATORY_ENFORCE_CHANNELS = BIT(6), + REGULATORY_WIPHY_SELF_MANAGED = BIT(7), }; struct ieee80211_freq_range { diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index d775245..3771e7d 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -774,6 +774,9 @@ * peer given by %NL80211_ATTR_MAC. Both peers must be on the base channel * when this command completes. * + * @NL80211_CMD_WIPHY_REG_CHANGE: Similar to %NL80211_CMD_REG_CHANGE, but used + * for indicating changes for devices with wiphy-specific regdom management + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -958,6 +961,8 @@ enum nl80211_commands { NL80211_CMD_TDLS_CHANNEL_SWITCH, NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH, + NL80211_CMD_WIPHY_REG_CHANGE, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ diff --git a/net/wireless/core.c b/net/wireless/core.c index 5295456..15036a4 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -559,6 +559,14 @@ int wiphy_register(struct wiphy *wiphy) BIT(NL80211_IFTYPE_MONITOR))))) return -EINVAL; + if (WARN_ON((wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) && + (wiphy->regulatory_flags & + (REGULATORY_CUSTOM_REG | REGULATORY_STRICT_REG | + REGULATORY_COUNTRY_IE_FOLLOW_POWER | + REGULATORY_COUNTRY_IE_IGNORE | + REGULATORY_DISABLE_BEACON_HINTS)))) + return -EINVAL; + if (WARN_ON(wiphy->coalesce && (!wiphy->coalesce->n_rules || !wiphy->coalesce->n_patterns) && diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 6e41777..de3a47a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -10942,25 +10942,9 @@ void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev, NL80211_MCGRP_SCAN, GFP_KERNEL); } -/* - * This can happen on global regulatory changes or device specific settings - * based on custom world regulatory domains. - */ -void nl80211_send_reg_change_event(struct regulatory_request *request) +static bool nl80211_reg_change_event_fill(struct sk_buff *msg, + struct regulatory_request *request) { - struct sk_buff *msg; - void *hdr; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE); - if (!hdr) { - nlmsg_free(msg); - return; - } - /* Userspace can always count this one always being set */ if (nla_put_u8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator)) goto nla_put_failure; @@ -10990,6 +10974,66 @@ void nl80211_send_reg_change_event(struct regulatory_request *request) nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx)) goto nla_put_failure; + return true; + +nla_put_failure: + return false; +} + +/* + * This can happen on global regulatory changes or device specific settings + * based on custom world regulatory domains. + */ +void nl80211_send_reg_change_event(struct regulatory_request *request) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE); + if (!hdr) { + nlmsg_free(msg); + return; + } + + if (nl80211_reg_change_event_fill(msg, request) == false) + goto nla_put_failure; + + genlmsg_end(msg, hdr); + + rcu_read_lock(); + genlmsg_multicast_allns(&nl80211_fam, msg, 0, + NL80211_MCGRP_REGULATORY, GFP_ATOMIC); + rcu_read_unlock(); + + return; + +nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + +void nl80211_send_wiphy_reg_change_event(struct regulatory_request *request) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_WIPHY_REG_CHANGE); + if (!hdr) { + nlmsg_free(msg); + return; + } + + if (nl80211_reg_change_event_fill(msg, request) == false) + goto nla_put_failure; + genlmsg_end(msg, hdr); rcu_read_lock(); diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 7ad70d6..b91b9c5 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -18,6 +18,7 @@ void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev, void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev, struct net_device *netdev); void nl80211_send_reg_change_event(struct regulatory_request *request); +void nl80211_send_wiphy_reg_change_event(struct regulatory_request *request); void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, size_t len, gfp_t gfp); diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 922d00a..18cf13b 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1307,6 +1307,9 @@ static bool ignore_reg_update(struct wiphy *wiphy, { struct regulatory_request *lr = get_last_request(); + if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) + return true; + if (!lr) { REG_DBG_PRINT("Ignoring regulatory request set by %s " "since last_request is not set\n", @@ -1370,6 +1373,9 @@ static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx, sband = wiphy->bands[reg_beacon->chan.band]; chan = &sband->channels[chan_idx]; + if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) + return; + if (likely(chan->center_freq != reg_beacon->chan.center_freq)) return; @@ -2431,6 +2437,9 @@ static void restore_regulatory_settings(bool reset_user) world_alpha2[1] = cfg80211_world_regdom->alpha2[1]; list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + if (rdev->wiphy.regulatory_flags & + REGULATORY_WIPHY_SELF_MANAGED) + continue; if (rdev->wiphy.regulatory_flags & REGULATORY_CUSTOM_REG) restore_custom_reg_settings(&rdev->wiphy); } @@ -2834,6 +2843,56 @@ int set_regdom(const struct ieee80211_regdomain *rd) return 0; } +int regulatory_set_wiphy_regd_rtnl(struct wiphy *wiphy, + struct ieee80211_regdomain *rd) +{ + const struct ieee80211_regdomain *regd; + const struct ieee80211_regdomain *tmp; + enum ieee80211_band band; + struct regulatory_request request = {}; + + if (WARN_ON(!wiphy || !rd)) + return -EINVAL; + + if (WARN(!(wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED), + "wiphy should have REGULATORY_WIPHY_SELF_MANAGED\n")) + return -EPERM; + + WARN_ON_ONCE(!lockdep_rtnl_is_held()); + + if (WARN(!is_valid_rd(rd), "Invalid regulatory domain detected\n")) { + print_regdomain_info(rd); + return -EINVAL; + } + + regd = reg_copy_regd(rd); + if (IS_ERR(regd)) + return PTR_ERR(regd); + + tmp = get_wiphy_regdom(wiphy); + rcu_assign_pointer(wiphy->regd, regd); + rcu_free_regdom(tmp); + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + handle_band_custom(wiphy, + wiphy->bands[band], + regd); + } + + reg_process_ht_flags(wiphy); + + request.wiphy_idx = get_wiphy_idx(wiphy); + request.alpha2[0] = rd->alpha2[0]; + request.alpha2[1] = rd->alpha2[1]; + request.initiator = NL80211_REGDOM_SET_BY_DRIVER; + + nl80211_send_wiphy_reg_change_event(&request); + + reg_check_channels(); + return 0; +} +EXPORT_SYMBOL(regulatory_set_wiphy_regd_rtnl); + void wiphy_regulatory_register(struct wiphy *wiphy) { struct regulatory_request *lr;