kfree(mc_filter);
}
- unregister_netdevice(vif->ndev);
+ cfg80211_unregister_netdevice(vif->ndev);
ar->num_vif--;
}
netdev_set_default_ethtool_ops(ndev, &ath6kl_ethtool_ops);
- if (register_netdevice(ndev))
+ if (cfg80211_register_netdevice(ndev))
goto err;
ar->avail_idx_map &= ~BIT(fw_vif_idx);
if (rc)
return rc;
}
- rc = register_netdevice(ndev);
+ rc = cfg80211_register_netdevice(ndev);
if (rc < 0) {
dev_err(&ndev->dev, "Failed to register netdev: %d\n", rc);
if (any_active && vif->mid != 0)
/* during unregister_netdevice cfg80211_leave may perform operations
* such as stop AP, disconnect, so we only clear the VIF afterwards
*/
- unregister_netdevice(ndev);
+ cfg80211_unregister_netdevice(ndev);
if (any_active && vif->mid != 0)
wmi_port_delete(wil, vif->mid);
INIT_WORK(&ifp->ndoffload_work, _brcmf_update_ndtable);
if (rtnl_locked)
- err = register_netdevice(ndev);
+ err = cfg80211_register_netdevice(ndev);
else
err = register_netdev(ndev);
if (err != 0) {
{
if (ndev->reg_state == NETREG_REGISTERED) {
if (rtnl_locked)
- unregister_netdevice(ndev);
+ cfg80211_unregister_netdevice(ndev);
else
unregister_netdev(ndev);
} else {
ndev = ifp->ndev;
ndev->netdev_ops = &brcmf_netdev_ops_mon;
- err = register_netdevice(ndev);
+ err = cfg80211_register_netdevice(ndev);
if (err)
bphy_err(drvr, "Failed to register %s device\n", ndev->name);
mutex_init(&priv->async_mutex);
/* Register network device */
- if (register_netdevice(dev)) {
+ if (cfg80211_register_netdevice(dev)) {
mwifiex_dbg(adapter, ERROR, "cannot register network device\n");
ret = -EFAULT;
goto err_reg_netdev;
netif_carrier_off(priv->netdev);
if (wdev->netdev->reg_state == NETREG_REGISTERED)
- unregister_netdevice(wdev->netdev);
+ cfg80211_unregister_netdevice(wdev->netdev);
if (priv->dfs_cac_workqueue) {
flush_workqueue(priv->dfs_cac_workqueue);
wilc_wfi_deinit_mon_interface(wl, true);
vif = netdev_priv(wdev->netdev);
cfg80211_stop_iface(wiphy, wdev, GFP_KERNEL);
- unregister_netdevice(vif->ndev);
+ cfg80211_unregister_netdevice(vif->ndev);
vif->monitor_flag = 0;
wilc_set_operation_mode(vif, 0, 0, 0);
wl->monitor_dev->netdev_ops = &wilc_wfi_netdev_ops;
wl->monitor_dev->needs_free_netdev = true;
- if (register_netdevice(wl->monitor_dev)) {
+ if (cfg80211_register_netdevice(wl->monitor_dev)) {
netdev_err(real_dev, "register_netdevice failed\n");
free_netdev(wl->monitor_dev);
return NULL;
return;
if (rtnl_locked)
- unregister_netdevice(wl->monitor_dev);
+ cfg80211_unregister_netdevice(wl->monitor_dev);
else
unregister_netdev(wl->monitor_dev);
wl->monitor_dev = NULL;
vif->priv.dev = ndev;
if (rtnl_locked)
- ret = register_netdevice(ndev);
+ ret = cfg80211_register_netdevice(ndev);
else
ret = register_netdev(ndev);
cancel_work_sync(&vif->high_pri_tx_work);
if (netdev->reg_state == NETREG_REGISTERED)
- unregister_netdevice(netdev);
+ cfg80211_unregister_netdevice(netdev);
if (qtnf_cmd_send_del_intf(vif))
pr_err("VIF%u.%u: failed to delete VIF\n", vif->mac->macid,
if (qtnf_hwcap_is_set(&mac->bus->hw_info, QLINK_HW_CAPAB_HW_BRIDGE)) {
ret = qtnf_cmd_netdev_changeupper(vif, vif->netdev->ifindex);
if (ret) {
- unregister_netdevice(vif->netdev);
+ cfg80211_unregister_netdevice(vif->netdev);
vif->netdev = NULL;
goto error_del_vif;
}
SET_NETDEV_DEV(dev, wiphy_dev(wiphy));
- ret = register_netdevice(dev);
+ ret = cfg80211_register_netdevice(dev);
if (ret) {
free_netdev(dev);
vif->netdev = NULL;
*
* @wiphy: pointer to hardware description
* @iftype: interface type
+ * @registered: is this wdev already registered with cfg80211
* @list: (private) Used to collect the interfaces
* @netdev: (private) Used to reference back to the netdev, may be %NULL
* @identifier: (private) Identifier used in nl80211 to identify this
struct mutex mtx;
- bool use_4addr, is_running;
+ bool use_4addr, is_running, registered;
u8 address[ETH_ALEN] __aligned(sizeof(u16));
* cfg80211_unregister_wdev - remove the given wdev
* @wdev: struct wireless_dev to remove
*
- * Call this function only for wdevs that have no netdev assigned,
- * e.g. P2P Devices. It removes the device from the list so that
- * it can no longer be used. It is necessary to call this function
- * even when cfg80211 requests the removal of the interface by
- * calling the del_virtual_intf() callback. The function must also
- * be called when the driver wishes to unregister the wdev, e.g.
- * when the device is unbound from the driver.
+ * This function removes the device so it can no longer be used. It is necessary
+ * to call this function even when cfg80211 requests the removal of the device
+ * by calling the del_virtual_intf() callback. The function must also be called
+ * when the driver wishes to unregister the wdev, e.g. when the hardware device
+ * is unbound from the driver.
*
* Requires the RTNL to be held.
*/
void cfg80211_unregister_wdev(struct wireless_dev *wdev);
/**
+ * cfg80211_register_netdevice - register the given netdev
+ * @dev: the netdev to register
+ *
+ * Note: In contexts coming from cfg80211 callbacks, you must call this rather
+ * than register_netdevice(), unregister_netdev() is impossible as the RTNL is
+ * held. Otherwise, both register_netdevice() and register_netdev() are usable
+ * instead as well.
+ */
+int cfg80211_register_netdevice(struct net_device *dev);
+
+/**
+ * cfg80211_unregister_netdevice - unregister the given netdev
+ * @dev: the netdev to register
+ *
+ * Note: In contexts coming from cfg80211 callbacks, you must call this rather
+ * than unregister_netdevice(), unregister_netdev() is impossible as the RTNL
+ * is held. Otherwise, both unregister_netdevice() and unregister_netdev() are
+ * usable instead as well.
+ */
+static inline void cfg80211_unregister_netdevice(struct net_device *dev)
+{
+ cfg80211_unregister_wdev(dev->ieee80211_ptr);
+}
+
+/**
* struct cfg80211_ft_event_params - FT Information Elements
* @ies: FT IEs
* @ies_len: length of the FT IE in bytes
ndev->min_mtu = 256;
ndev->max_mtu = local->hw.max_mtu;
- ret = register_netdevice(ndev);
+ ret = cfg80211_register_netdevice(ndev);
if (ret) {
free_netdev(ndev);
return ret;
synchronize_rcu();
- if (sdata->dev) {
- unregister_netdevice(sdata->dev);
- } else {
- cfg80211_unregister_wdev(&sdata->wdev);
+ cfg80211_unregister_wdev(&sdata->wdev);
+
+ if (!sdata->dev) {
ieee80211_teardown_sdata(sdata);
kfree(sdata);
}
wdev->cqm_config = NULL;
}
-static void __cfg80211_unregister_wdev(struct wireless_dev *wdev, bool sync)
+static void _cfg80211_unregister_wdev(struct wireless_dev *wdev,
+ bool unregister_netdev)
{
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
nl80211_notify_iface(rdev, wdev, NL80211_CMD_DEL_INTERFACE);
+ wdev->registered = false;
+
+ if (wdev->netdev) {
+ sysfs_remove_link(&wdev->netdev->dev.kobj, "phy80211");
+ if (unregister_netdev)
+ unregister_netdevice(wdev->netdev);
+ }
+
list_del_rcu(&wdev->list);
- if (sync)
- synchronize_rcu();
+ synchronize_net();
rdev->devlist_generation++;
cfg80211_mlme_purge_registrations(wdev);
flush_work(&wdev->disconnect_wk);
cfg80211_cqm_config_free(wdev);
+
+ /*
+ * Ensure that all events have been processed and
+ * freed.
+ */
+ cfg80211_process_wdev_events(wdev);
+
+ if (WARN_ON(wdev->current_bss)) {
+ cfg80211_unhold_bss(wdev->current_bss);
+ cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
+ wdev->current_bss = NULL;
+ }
}
void cfg80211_unregister_wdev(struct wireless_dev *wdev)
{
- if (WARN_ON(wdev->netdev))
- return;
-
- __cfg80211_unregister_wdev(wdev, true);
+ _cfg80211_unregister_wdev(wdev, true);
}
EXPORT_SYMBOL(cfg80211_unregister_wdev);
wdev->identifier = ++rdev->wdev_id;
list_add_rcu(&wdev->list, &rdev->wiphy.wdev_list);
rdev->devlist_generation++;
+ wdev->registered = true;
nl80211_notify_iface(rdev, wdev, NL80211_CMD_NEW_INTERFACE);
}
+int cfg80211_register_netdevice(struct net_device *dev)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev;
+ int ret;
+
+ ASSERT_RTNL();
+
+ if (WARN_ON(!wdev))
+ return -EINVAL;
+
+ rdev = wiphy_to_rdev(wdev->wiphy);
+
+ lockdep_assert_held(&rdev->wiphy.mtx);
+
+ /* we'll take care of this */
+ wdev->registered = true;
+ ret = register_netdevice(dev);
+ if (ret)
+ goto out;
+
+ if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj,
+ "phy80211")) {
+ pr_err("failed to add phy80211 symlink to netdev!\n");
+ unregister_netdevice(dev);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ cfg80211_register_wdev(rdev, wdev);
+ ret = 0;
+out:
+ if (ret)
+ wdev->registered = false;
+ return ret;
+}
+EXPORT_SYMBOL(cfg80211_register_netdevice);
+
static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
unsigned long state, void *ptr)
{
cfg80211_init_wdev(wdev);
break;
case NETDEV_REGISTER:
+ if (!wdev->registered)
+ cfg80211_register_wdev(rdev, wdev);
+ break;
+ case NETDEV_UNREGISTER:
/*
- * NB: cannot take rdev->mtx here because this may be
- * called within code protected by it when interfaces
- * are added with nl80211.
+ * It is possible to get NETDEV_UNREGISTER multiple times,
+ * so check wdev->registered.
*/
- if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj,
- "phy80211")) {
- pr_err("failed to add phy80211 symlink to netdev!\n");
- }
-
- cfg80211_register_wdev(rdev, wdev);
+ if (wdev->registered)
+ _cfg80211_unregister_wdev(wdev, false);
break;
case NETDEV_GOING_DOWN:
cfg80211_leave(rdev, wdev);
wdev->ps = false;
}
break;
- case NETDEV_UNREGISTER:
- /*
- * It is possible to get NETDEV_UNREGISTER
- * multiple times. To detect that, check
- * that the interface is still on the list
- * of registered interfaces, and only then
- * remove and clean it up.
- */
- if (!list_empty(&wdev->list)) {
- __cfg80211_unregister_wdev(wdev, false);
- sysfs_remove_link(&dev->dev.kobj, "phy80211");
- }
- /*
- * synchronise (so that we won't find this netdev
- * from other code any more) and then clear the list
- * head so that the above code can safely check for
- * !list_empty() to avoid double-cleanup.
- */
- synchronize_rcu();
- INIT_LIST_HEAD(&wdev->list);
- /*
- * Ensure that all events have been processed and
- * freed.
- */
- cfg80211_process_wdev_events(wdev);
-
- if (WARN_ON(wdev->current_bss)) {
- cfg80211_unhold_bss(wdev->current_bss);
- cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
- wdev->current_bss = NULL;
- }
- break;
case NETDEV_PRE_UP:
if (!cfg80211_iftype_allowed(wdev->wiphy, wdev->iftype,
wdev->use_4addr, 0))