Commit 77cbf790 authored by Johannes Berg's avatar Johannes Berg

nl80211: fix locking for wireless device netns change

We have all the network interfaces marked as netns-local
since the only reasonable thing to do right now is to set
a whole device, including all netdevs, into a different
network namespace. For this reason, we also have our own
way of changing the network namespace.

Unfortunately, the RTNL locking changes broke this, and
it now results in many RTNL assertions. The trivial fix
for those (just hold RTNL for the changes) however leads
to deadlocks in the cfg80211 netdev notifier.

Since we only need the wiphy, and that's still protected
by the RTNL, add a new NL80211_FLAG_NO_WIPHY_MTX flag to
the nl80211 ops and use it to _not_ take the wiphy mutex
but only the RTNL. This way, the notifier does all the
work necessary during unregistration/registration of the
netdevs from the old and in the new namespace.
Reported-by: default avatarSid Hayn <sidhayn@gmail.com>
Fixes: a05829a7 ("cfg80211: avoid holding the RTNL when calling the driver")
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Link: https://lore.kernel.org/r/20210310215839.eadf7c43781b.I5fc6cf6676f800ab8008e03bbea9c3349b02d804@changeidSigned-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 58d25626
...@@ -14789,6 +14789,7 @@ static int nl80211_set_tid_config(struct sk_buff *skb, ...@@ -14789,6 +14789,7 @@ static int nl80211_set_tid_config(struct sk_buff *skb,
#define NL80211_FLAG_NEED_WDEV_UP (NL80211_FLAG_NEED_WDEV |\ #define NL80211_FLAG_NEED_WDEV_UP (NL80211_FLAG_NEED_WDEV |\
NL80211_FLAG_CHECK_NETDEV_UP) NL80211_FLAG_CHECK_NETDEV_UP)
#define NL80211_FLAG_CLEAR_SKB 0x20 #define NL80211_FLAG_CLEAR_SKB 0x20
#define NL80211_FLAG_NO_WIPHY_MTX 0x40
static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb, static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
struct genl_info *info) struct genl_info *info)
...@@ -14840,7 +14841,7 @@ static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb, ...@@ -14840,7 +14841,7 @@ static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
info->user_ptr[0] = rdev; info->user_ptr[0] = rdev;
} }
if (rdev) { if (rdev && !(ops->internal_flags & NL80211_FLAG_NO_WIPHY_MTX)) {
wiphy_lock(&rdev->wiphy); wiphy_lock(&rdev->wiphy);
/* we keep the mutex locked until post_doit */ /* we keep the mutex locked until post_doit */
__release(&rdev->wiphy.mtx); __release(&rdev->wiphy.mtx);
...@@ -14865,7 +14866,8 @@ static void nl80211_post_doit(const struct genl_ops *ops, struct sk_buff *skb, ...@@ -14865,7 +14866,8 @@ static void nl80211_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
} }
} }
if (info->user_ptr[0]) { if (info->user_ptr[0] &&
!(ops->internal_flags & NL80211_FLAG_NO_WIPHY_MTX)) {
struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct cfg80211_registered_device *rdev = info->user_ptr[0];
/* we kept the mutex locked since pre_doit */ /* we kept the mutex locked since pre_doit */
...@@ -15329,7 +15331,9 @@ static const struct genl_small_ops nl80211_small_ops[] = { ...@@ -15329,7 +15331,9 @@ static const struct genl_small_ops nl80211_small_ops[] = {
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
.doit = nl80211_wiphy_netns, .doit = nl80211_wiphy_netns,
.flags = GENL_UNS_ADMIN_PERM, .flags = GENL_UNS_ADMIN_PERM,
.internal_flags = NL80211_FLAG_NEED_WIPHY, .internal_flags = NL80211_FLAG_NEED_WIPHY |
NL80211_FLAG_NEED_RTNL |
NL80211_FLAG_NO_WIPHY_MTX,
}, },
{ {
.cmd = NL80211_CMD_GET_SURVEY, .cmd = NL80211_CMD_GET_SURVEY,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment