Commit 024fcf5e authored by Johannes Berg's avatar Johannes Berg
Browse files

nl80211: use RCU to read regdom in reg get/dump

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: default avatarJohannes Berg <johannes.berg@intel.com>
parent b59fb546
Loading
Loading
Loading
Loading
+17 −15
Original line number Diff line number Diff line
@@ -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 @@ static int nl80211_get_reg_do(struct sk_buff *skb, struct genl_info *info)
	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;
}