cfg80211: vastly simplify locking
Virtually all code paths in cfg80211 already (need to) hold
the RTNL. As such, there's little point in having another
four mutexes for various parts of the code, they just cause
lock ordering issues (and much of the time, the RTNL and a
few of the others need thus be held.)
Simplify all this by getting rid of the extra four mutexes
and just use the RTNL throughout. Only a few code changes
were needed to do this and we can get rid of a work struct
for bonus points.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 5bcf3a5..74cdb1a 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -59,7 +59,7 @@
int wiphy_idx = -1;
int ifidx = -1;
- assert_cfg80211_lock();
+ ASSERT_RTNL();
if (!have_ifidx && !have_wdev_id)
return ERR_PTR(-EINVAL);
@@ -80,7 +80,6 @@
if (have_wdev_id && rdev->wiphy_idx != wiphy_idx)
continue;
- mutex_lock(&rdev->devlist_mtx);
list_for_each_entry(wdev, &rdev->wdev_list, list) {
if (have_ifidx && wdev->netdev &&
wdev->netdev->ifindex == ifidx) {
@@ -92,7 +91,6 @@
break;
}
}
- mutex_unlock(&rdev->devlist_mtx);
if (result)
break;
@@ -109,7 +107,7 @@
struct cfg80211_registered_device *rdev = NULL, *tmp;
struct net_device *netdev;
- assert_cfg80211_lock();
+ ASSERT_RTNL();
if (!attrs[NL80211_ATTR_WIPHY] &&
!attrs[NL80211_ATTR_IFINDEX] &&
@@ -128,14 +126,12 @@
tmp = cfg80211_rdev_by_wiphy_idx(wdev_id >> 32);
if (tmp) {
/* make sure wdev exists */
- mutex_lock(&tmp->devlist_mtx);
list_for_each_entry(wdev, &tmp->wdev_list, list) {
if (wdev->identifier != (u32)wdev_id)
continue;
found = true;
break;
}
- mutex_unlock(&tmp->devlist_mtx);
if (!found)
tmp = NULL;
@@ -182,19 +178,6 @@
/*
* This function returns a pointer to the driver
* that the genl_info item that is passed refers to.
- * If successful, it returns non-NULL and also locks
- * the driver's mutex!
- *
- * This means that you need to call cfg80211_unlock_rdev()
- * before being allowed to acquire &cfg80211_mutex!
- *
- * This is necessary because we need to lock the global
- * mutex to get an item off the list safely, and then
- * we lock the rdev mutex so it doesn't go away under us.
- *
- * We don't want to keep cfg80211_mutex locked
- * for all the time in order to allow requests on
- * other interfaces to go through at the same time.
*
* The result of this can be a PTR_ERR and hence must
* be checked with IS_ERR() for errors.
@@ -202,20 +185,7 @@
static struct cfg80211_registered_device *
cfg80211_get_dev_from_info(struct net *netns, struct genl_info *info)
{
- struct cfg80211_registered_device *rdev;
-
- mutex_lock(&cfg80211_mutex);
- rdev = __cfg80211_rdev_from_attrs(netns, info->attrs);
-
- /* if it is not an error we grab the lock on
- * it to assure it won't be going away while
- * we operate on it */
- if (!IS_ERR(rdev))
- mutex_lock(&rdev->mtx);
-
- mutex_unlock(&cfg80211_mutex);
-
- return rdev;
+ return __cfg80211_rdev_from_attrs(netns, info->attrs);
}
/* policy for the attributes */
@@ -456,7 +426,6 @@
int err;
rtnl_lock();
- mutex_lock(&cfg80211_mutex);
if (!cb->args[0]) {
err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
@@ -485,14 +454,12 @@
*rdev = wiphy_to_dev(wiphy);
*wdev = NULL;
- mutex_lock(&(*rdev)->devlist_mtx);
list_for_each_entry(tmp, &(*rdev)->wdev_list, list) {
if (tmp->identifier == cb->args[1]) {
*wdev = tmp;
break;
}
}
- mutex_unlock(&(*rdev)->devlist_mtx);
if (!*wdev) {
err = -ENODEV;
@@ -500,19 +467,14 @@
}
}
- cfg80211_lock_rdev(*rdev);
-
- mutex_unlock(&cfg80211_mutex);
return 0;
out_unlock:
- mutex_unlock(&cfg80211_mutex);
rtnl_unlock();
return err;
}
static void nl80211_finish_wdev_dump(struct cfg80211_registered_device *rdev)
{
- cfg80211_unlock_rdev(rdev);
rtnl_unlock();
}
@@ -1568,7 +1530,7 @@
struct nlattr **tb = nl80211_fam.attrbuf;
int res;
- mutex_lock(&cfg80211_mutex);
+ rtnl_lock();
res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
tb, nl80211_fam.maxattr, nl80211_policy);
if (res == 0) {
@@ -1582,10 +1544,8 @@
int ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
netdev = dev_get_by_index(sock_net(skb->sk), ifidx);
- if (!netdev) {
- mutex_unlock(&cfg80211_mutex);
+ if (!netdev)
return -ENODEV;
- }
if (netdev->ieee80211_ptr) {
dev = wiphy_to_dev(
netdev->ieee80211_ptr->wiphy);
@@ -1629,7 +1589,6 @@
!skb->len &&
cb->min_dump_alloc < 4096) {
cb->min_dump_alloc = 4096;
- mutex_unlock(&cfg80211_mutex);
return 1;
}
idx--;
@@ -1638,7 +1597,7 @@
} while (cb->args[1] > 0);
break;
}
- mutex_unlock(&cfg80211_mutex);
+ rtnl_unlock();
cb->args[0] = idx;
@@ -1793,7 +1752,6 @@
if (result)
return result;
- mutex_lock(&rdev->devlist_mtx);
switch (iftype) {
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO:
@@ -1817,7 +1775,6 @@
default:
result = -EINVAL;
}
- mutex_unlock(&rdev->devlist_mtx);
return result;
}
@@ -1866,6 +1823,8 @@
u32 frag_threshold = 0, rts_threshold = 0;
u8 coverage_class = 0;
+ ASSERT_RTNL();
+
/*
* Try to find the wiphy and netdev. Normally this
* function shouldn't need the netdev, but this is
@@ -1875,31 +1834,25 @@
* also passed a netdev to set_wiphy, so that it is
* possible to let that go to the right netdev!
*/
- mutex_lock(&cfg80211_mutex);
if (info->attrs[NL80211_ATTR_IFINDEX]) {
int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
netdev = dev_get_by_index(genl_info_net(info), ifindex);
- if (netdev && netdev->ieee80211_ptr) {
+ if (netdev && netdev->ieee80211_ptr)
rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy);
- mutex_lock(&rdev->mtx);
- } else
+ else
netdev = NULL;
}
if (!netdev) {
rdev = __cfg80211_rdev_from_attrs(genl_info_net(info),
info->attrs);
- if (IS_ERR(rdev)) {
- mutex_unlock(&cfg80211_mutex);
+ if (IS_ERR(rdev))
return PTR_ERR(rdev);
- }
wdev = NULL;
netdev = NULL;
result = 0;
-
- mutex_lock(&rdev->mtx);
} else
wdev = netdev->ieee80211_ptr;
@@ -1912,8 +1865,6 @@
result = cfg80211_dev_rename(
rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
- mutex_unlock(&cfg80211_mutex);
-
if (result)
goto bad_res;
@@ -2120,7 +2071,6 @@
}
bad_res:
- mutex_unlock(&rdev->mtx);
if (netdev)
dev_put(netdev);
return result;
@@ -2218,7 +2168,7 @@
struct cfg80211_registered_device *rdev;
struct wireless_dev *wdev;
- mutex_lock(&cfg80211_mutex);
+ rtnl_lock();
list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
continue;
@@ -2228,7 +2178,6 @@
}
if_idx = 0;
- mutex_lock(&rdev->devlist_mtx);
list_for_each_entry(wdev, &rdev->wdev_list, list) {
if (if_idx < if_start) {
if_idx++;
@@ -2237,17 +2186,15 @@
if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI,
rdev, wdev) < 0) {
- mutex_unlock(&rdev->devlist_mtx);
goto out;
}
if_idx++;
}
- mutex_unlock(&rdev->devlist_mtx);
wp_idx++;
}
out:
- mutex_unlock(&cfg80211_mutex);
+ rtnl_unlock();
cb->args[0] = wp_idx;
cb->args[1] = if_idx;
@@ -2480,11 +2427,9 @@
INIT_LIST_HEAD(&wdev->mgmt_registrations);
spin_lock_init(&wdev->mgmt_registrations_lock);
- mutex_lock(&rdev->devlist_mtx);
wdev->identifier = ++rdev->wdev_id;
list_add_rcu(&wdev->list, &rdev->wdev_list);
rdev->devlist_generation++;
- mutex_unlock(&rdev->devlist_mtx);
break;
default:
break;
@@ -2993,8 +2938,6 @@
struct wireless_dev *wdev;
bool ret = false;
- mutex_lock(&rdev->devlist_mtx);
-
list_for_each_entry(wdev, &rdev->wdev_list, list) {
if (wdev->iftype != NL80211_IFTYPE_AP &&
wdev->iftype != NL80211_IFTYPE_P2P_GO)
@@ -3008,8 +2951,6 @@
break;
}
- mutex_unlock(&rdev->devlist_mtx);
-
return ret;
}
@@ -3171,13 +3112,10 @@
params.radar_required = true;
}
- mutex_lock(&rdev->devlist_mtx);
err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
params.chandef.chan,
CHAN_MODE_SHARED,
radar_detect_width);
- mutex_unlock(&rdev->devlist_mtx);
-
if (err)
return err;
@@ -4914,18 +4852,13 @@
void *hdr = NULL;
struct nlattr *nl_reg_rules;
unsigned int i;
- int err = -EINVAL;
-
- mutex_lock(&cfg80211_mutex);
if (!cfg80211_regdomain)
- goto out;
+ return -EINVAL;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
- if (!msg) {
- err = -ENOBUFS;
- goto out;
- }
+ if (!msg)
+ return -ENOBUFS;
hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
NL80211_CMD_GET_REG);
@@ -4984,8 +4917,7 @@
nla_nest_end(msg, nl_reg_rules);
genlmsg_end(msg, hdr);
- err = genlmsg_reply(msg, info);
- goto out;
+ return genlmsg_reply(msg, info);
nla_put_failure_rcu:
rcu_read_unlock();
@@ -4993,10 +4925,7 @@
genlmsg_cancel(msg, hdr);
put_failure:
nlmsg_free(msg);
- err = -EMSGSIZE;
-out:
- mutex_unlock(&cfg80211_mutex);
- return err;
+ return -EMSGSIZE;
}
static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
@@ -5062,12 +4991,9 @@
}
}
- mutex_lock(&cfg80211_mutex);
-
r = set_regdom(rd);
/* set_regdom took ownership */
rd = NULL;
- mutex_unlock(&cfg80211_mutex);
bad_reg:
kfree(rd);
@@ -5117,7 +5043,6 @@
if (!rdev->ops->scan)
return -EOPNOTSUPP;
- mutex_lock(&rdev->sched_scan_mtx);
if (rdev->scan_req) {
err = -EBUSY;
goto unlock;
@@ -5303,7 +5228,6 @@
}
unlock:
- mutex_unlock(&rdev->sched_scan_mtx);
return err;
}
@@ -5375,8 +5299,6 @@
if (ie_len > wiphy->max_sched_scan_ie_len)
return -EINVAL;
- mutex_lock(&rdev->sched_scan_mtx);
-
if (rdev->sched_scan_req) {
err = -EINPROGRESS;
goto out;
@@ -5544,7 +5466,6 @@
out_free:
kfree(request);
out:
- mutex_unlock(&rdev->sched_scan_mtx);
return err;
}
@@ -5552,17 +5473,12 @@
struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
- int err;
if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
!rdev->ops->sched_scan_stop)
return -EOPNOTSUPP;
- mutex_lock(&rdev->sched_scan_mtx);
- err = __cfg80211_stop_sched_scan(rdev, false);
- mutex_unlock(&rdev->sched_scan_mtx);
-
- return err;
+ return __cfg80211_stop_sched_scan(rdev, false);
}
static int nl80211_start_radar_detection(struct sk_buff *skb,
@@ -5594,12 +5510,11 @@
if (!rdev->ops->start_radar_detection)
return -EOPNOTSUPP;
- mutex_lock(&rdev->devlist_mtx);
err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
chandef.chan, CHAN_MODE_SHARED,
BIT(chandef.width));
if (err)
- goto err_locked;
+ return err;
err = rdev->ops->start_radar_detection(&rdev->wiphy, dev, &chandef);
if (!err) {
@@ -5607,9 +5522,6 @@
wdev->cac_started = true;
wdev->cac_start_time = jiffies;
}
-err_locked:
- mutex_unlock(&rdev->devlist_mtx);
-
return err;
}
@@ -6472,6 +6384,8 @@
void *data = NULL;
int data_len = 0;
+ rtnl_lock();
+
if (cb->args[0]) {
/*
* 0 is a valid index, but not valid for args[0],
@@ -6483,18 +6397,16 @@
nl80211_fam.attrbuf, nl80211_fam.maxattr,
nl80211_policy);
if (err)
- return err;
+ goto out_err;
- mutex_lock(&cfg80211_mutex);
rdev = __cfg80211_rdev_from_attrs(sock_net(skb->sk),
nl80211_fam.attrbuf);
if (IS_ERR(rdev)) {
- mutex_unlock(&cfg80211_mutex);
- return PTR_ERR(rdev);
+ err = PTR_ERR(rdev);
+ goto out_err;
}
phy_idx = rdev->wiphy_idx;
rdev = NULL;
- mutex_unlock(&cfg80211_mutex);
if (nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA])
cb->args[1] =
@@ -6506,14 +6418,11 @@
data_len = nla_len((void *)cb->args[1]);
}
- mutex_lock(&cfg80211_mutex);
rdev = cfg80211_rdev_by_wiphy_idx(phy_idx);
if (!rdev) {
- mutex_unlock(&cfg80211_mutex);
- return -ENOENT;
+ err = -ENOENT;
+ goto out_err;
}
- cfg80211_lock_rdev(rdev);
- mutex_unlock(&cfg80211_mutex);
if (!rdev->ops->testmode_dump) {
err = -EOPNOTSUPP;
@@ -6554,7 +6463,7 @@
/* see above */
cb->args[0] = phy_idx + 1;
out_err:
- cfg80211_unlock_rdev(rdev);
+ rtnl_unlock();
return err;
}
@@ -8189,9 +8098,7 @@
if (wdev->p2p_started)
return 0;
- mutex_lock(&rdev->devlist_mtx);
err = cfg80211_can_add_interface(rdev, wdev->iftype);
- mutex_unlock(&rdev->devlist_mtx);
if (err)
return err;
@@ -8200,9 +8107,7 @@
return err;
wdev->p2p_started = true;
- mutex_lock(&rdev->devlist_mtx);
rdev->opencount++;
- mutex_unlock(&rdev->devlist_mtx);
return 0;
}
@@ -8218,11 +8123,7 @@
if (!rdev->ops->stop_p2p_device)
return -EOPNOTSUPP;
- mutex_lock(&rdev->devlist_mtx);
- mutex_lock(&rdev->sched_scan_mtx);
cfg80211_stop_p2p_device(rdev, wdev);
- mutex_unlock(&rdev->sched_scan_mtx);
- mutex_unlock(&rdev->devlist_mtx);
return 0;
}
@@ -8365,11 +8266,11 @@
info->user_ptr[0] = rdev;
} else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV ||
ops->internal_flags & NL80211_FLAG_NEED_WDEV) {
- mutex_lock(&cfg80211_mutex);
+ ASSERT_RTNL();
+
wdev = __cfg80211_wdev_from_attrs(genl_info_net(info),
info->attrs);
if (IS_ERR(wdev)) {
- mutex_unlock(&cfg80211_mutex);
if (rtnl)
rtnl_unlock();
return PTR_ERR(wdev);
@@ -8380,7 +8281,6 @@
if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) {
if (!dev) {
- mutex_unlock(&cfg80211_mutex);
if (rtnl)
rtnl_unlock();
return -EINVAL;
@@ -8394,7 +8294,6 @@
if (dev) {
if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP &&
!netif_running(dev)) {
- mutex_unlock(&cfg80211_mutex);
if (rtnl)
rtnl_unlock();
return -ENETDOWN;
@@ -8403,17 +8302,12 @@
dev_hold(dev);
} else if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP) {
if (!wdev->p2p_started) {
- mutex_unlock(&cfg80211_mutex);
if (rtnl)
rtnl_unlock();
return -ENETDOWN;
}
}
- cfg80211_lock_rdev(rdev);
-
- mutex_unlock(&cfg80211_mutex);
-
info->user_ptr[0] = rdev;
}
@@ -8423,8 +8317,6 @@
static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
struct genl_info *info)
{
- if (info->user_ptr[0])
- cfg80211_unlock_rdev(info->user_ptr[0]);
if (info->user_ptr[1]) {
if (ops->internal_flags & NL80211_FLAG_NEED_WDEV) {
struct wireless_dev *wdev = info->user_ptr[1];
@@ -8446,7 +8338,8 @@
.dumpit = nl80211_dump_wiphy,
.policy = nl80211_policy,
/* can be retrieved by unprivileged users */
- .internal_flags = NL80211_FLAG_NEED_WIPHY,
+ .internal_flags = NL80211_FLAG_NEED_WIPHY |
+ NL80211_FLAG_NEED_RTNL,
},
{
.cmd = NL80211_CMD_SET_WIPHY,
@@ -8461,7 +8354,8 @@
.dumpit = nl80211_dump_interface,
.policy = nl80211_policy,
/* can be retrieved by unprivileged users */
- .internal_flags = NL80211_FLAG_NEED_WDEV,
+ .internal_flags = NL80211_FLAG_NEED_WDEV |
+ NL80211_FLAG_NEED_RTNL,
},
{
.cmd = NL80211_CMD_SET_INTERFACE,
@@ -8620,6 +8514,7 @@
.cmd = NL80211_CMD_GET_REG,
.doit = nl80211_get_reg,
.policy = nl80211_policy,
+ .internal_flags = NL80211_FLAG_NEED_RTNL,
/* can be retrieved by unprivileged users */
},
{
@@ -8627,6 +8522,7 @@
.doit = nl80211_set_reg,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_RTNL,
},
{
.cmd = NL80211_CMD_REQ_SET_REG,
@@ -9082,8 +8978,6 @@
struct nlattr *nest;
int i;
- lockdep_assert_held(&rdev->sched_scan_mtx);
-
if (WARN_ON(!req))
return 0;