diff --git a/net/wireless/core.c b/net/wireless/core.c index 043bb57b0556..9ee1c36f1262 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -153,9 +153,25 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, return 0; } +static int cfg80211_switch_wdev_netns(struct wireless_dev *wdev, + struct net *net) +{ + int err; + + if (!wdev->netdev) + return 0; + + wdev->netdev->netns_immutable = false; + err = dev_change_net_namespace(wdev->netdev, net, "wlan%d"); + wdev->netdev->netns_immutable = true; + + return err; +} + int cfg80211_switch_netns(struct cfg80211_registered_device *rdev, struct net *net) { + struct net *old_net = wiphy_net(&rdev->wiphy); struct wireless_dev *wdev; int err = 0; @@ -163,58 +179,54 @@ int cfg80211_switch_netns(struct cfg80211_registered_device *rdev, return -EOPNOTSUPP; list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { - if (!wdev->netdev) - continue; - wdev->netdev->netns_immutable = false; - err = dev_change_net_namespace(wdev->netdev, net, "wlan%d"); - wdev->netdev->netns_immutable = true; + err = cfg80211_switch_wdev_netns(wdev, net); if (err) - break; + goto undo; } - if (err) { - /* failed -- clean up to old netns */ - net = wiphy_net(&rdev->wiphy); - - list_for_each_entry_continue_reverse(wdev, - &rdev->wiphy.wdev_list, - list) { + scoped_guard(wiphy, &rdev->wiphy) { + list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { if (!wdev->netdev) continue; - wdev->netdev->netns_immutable = false; - err = dev_change_net_namespace(wdev->netdev, net, - "wlan%d"); - WARN_ON(err); - wdev->netdev->netns_immutable = true; + nl80211_notify_iface(rdev, wdev, + NL80211_CMD_DEL_INTERFACE); } - return err; + nl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY); + + wiphy_net_set(&rdev->wiphy, net); + + /* this only fails on allocation failure */ + err = device_rename(&rdev->wiphy.dev, + dev_name(&rdev->wiphy.dev)); + if (err) + wiphy_net_set(&rdev->wiphy, old_net); + + nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY); + + list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { + if (!wdev->netdev) + continue; + nl80211_notify_iface(rdev, wdev, + NL80211_CMD_NEW_INTERFACE); + } } - guard(wiphy)(&rdev->wiphy); + if (!err) + return 0; - list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { - if (!wdev->netdev) - continue; - nl80211_notify_iface(rdev, wdev, NL80211_CMD_DEL_INTERFACE); - } + /* set to the last one to undo all of them */ + wdev = list_entry(&rdev->wiphy.wdev_list, typeof(*wdev), list); +undo: + /* + * Move back everything, if this fails again (allocation failures) + * then things get stuck in different network namespaces. + */ + list_for_each_entry_continue_reverse(wdev, &rdev->wiphy.wdev_list, + list) + WARN_ON(cfg80211_switch_wdev_netns(wdev, old_net)); - nl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY); - - wiphy_net_set(&rdev->wiphy, net); - - err = device_rename(&rdev->wiphy.dev, dev_name(&rdev->wiphy.dev)); - WARN_ON(err); - - nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY); - - list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { - if (!wdev->netdev) - continue; - nl80211_notify_iface(rdev, wdev, NL80211_CMD_NEW_INTERFACE); - } - - return 0; + return err; } static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data)