nl80211: use RCU to read regdom in reg get/dump
authorJohannes Berg <johannes.berg@intel.com>
Mon, 14 Feb 2022 09:18:20 +0000 (10:18 +0100)
committerJohannes Berg <johannes.berg@intel.com>
Wed, 16 Feb 2022 14:40:11 +0000 (15:40 +0100)
Use RCU here to read the regdomain, this will allow us
to remove the RTNL locking from the setter.

Note in nl80211_get_reg_do() we still need the RTNL to
do the wiphy lookup.

Link: https://lore.kernel.org/r/20220214101820.5d4acbcf2a46.Ibfc91980439862125e983d9adeebaba73fe38e2d@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/wireless/nl80211.c

index 578bff9..56ff332 100644 (file)
@@ -7948,6 +7948,7 @@ static int nl80211_get_reg_do(struct sk_buff *skb, struct genl_info *info)
        struct cfg80211_registered_device *rdev;
        struct wiphy *wiphy = NULL;
        struct sk_buff *msg;
+       int err = -EMSGSIZE;
        void *hdr;
 
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
@@ -7966,34 +7967,35 @@ static int nl80211_get_reg_do(struct sk_buff *skb, struct genl_info *info)
 
                rdev = cfg80211_get_dev_from_info(genl_info_net(info), info);
                if (IS_ERR(rdev)) {
-                       nlmsg_free(msg);
-                       rtnl_unlock();
-                       return PTR_ERR(rdev);
+                       err = PTR_ERR(rdev);
+                       goto nla_put_failure;
                }
 
                wiphy = &rdev->wiphy;
                self_managed = wiphy->regulatory_flags &
                               REGULATORY_WIPHY_SELF_MANAGED;
+
+               rcu_read_lock();
+
                regdom = get_wiphy_regdom(wiphy);
 
                /* a self-managed-reg device must have a private regdom */
                if (WARN_ON(!regdom && self_managed)) {
-                       nlmsg_free(msg);
-                       rtnl_unlock();
-                       return -EINVAL;
+                       err = -EINVAL;
+                       goto nla_put_failure_rcu;
                }
 
                if (regdom &&
                    nla_put_u32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy)))
-                       goto nla_put_failure;
+                       goto nla_put_failure_rcu;
+       } else {
+               rcu_read_lock();
        }
 
        if (!wiphy && reg_last_request_cell_base() &&
            nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE,
                        NL80211_USER_REG_HINT_CELL_BASE))
-               goto nla_put_failure;
-
-       rcu_read_lock();
+               goto nla_put_failure_rcu;
 
        if (!regdom)
                regdom = rcu_dereference(cfg80211_regdomain);
@@ -8013,7 +8015,7 @@ nla_put_failure:
        rtnl_unlock();
 put_failure:
        nlmsg_free(msg);
-       return -EMSGSIZE;
+       return err;
 }
 
 static int nl80211_send_regdom(struct sk_buff *msg, struct netlink_callback *cb,
@@ -8059,19 +8061,19 @@ static int nl80211_get_reg_dump(struct sk_buff *skb,
        struct cfg80211_registered_device *rdev;
        int err, reg_idx, start = cb->args[2];
 
-       rtnl_lock();
+       rcu_read_lock();
 
        if (cfg80211_regdomain && start == 0) {
                err = nl80211_send_regdom(skb, cb, cb->nlh->nlmsg_seq,
                                          NLM_F_MULTI, NULL,
-                                         rtnl_dereference(cfg80211_regdomain));
+                                         rcu_dereference(cfg80211_regdomain));
                if (err < 0)
                        goto out_err;
        }
 
        /* the global regdom is idx 0 */
        reg_idx = 1;
-       list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+       list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
                regdom = get_wiphy_regdom(&rdev->wiphy);
                if (!regdom)
                        continue;
@@ -8090,7 +8092,7 @@ static int nl80211_get_reg_dump(struct sk_buff *skb,
        cb->args[2] = reg_idx;
        err = skb->len;
 out_err:
-       rtnl_unlock();
+       rcu_read_unlock();
        return err;
 }