diff --git a/net/mac80211/ieee80211.c b/net/mac80211/ieee80211.c index ccf84634f156..52638194e45f 100644 --- a/net/mac80211/ieee80211.c +++ b/net/mac80211/ieee80211.c @@ -265,7 +265,6 @@ void ieee80211_if_mgmt_setup(struct net_device *dev) dev->open = ieee80211_mgmt_open; dev->stop = ieee80211_mgmt_stop; dev->type = ARPHRD_IEEE80211_PRISM; - dev->uninit = ieee80211_if_reinit; dev->destructor = ieee80211_if_free; } @@ -551,7 +550,6 @@ void ieee80211_if_setup(struct net_device *dev) dev->change_mtu = ieee80211_change_mtu; dev->open = ieee80211_open; dev->stop = ieee80211_stop; - dev->uninit = ieee80211_if_reinit; dev->destructor = ieee80211_if_free; } @@ -1242,6 +1240,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) goto fail_dev; ieee80211_debugfs_add_netdev(IEEE80211_DEV_TO_SUB_IF(local->mdev)); + ieee80211_if_set_type(local->mdev, IEEE80211_IF_TYPE_AP); result = ieee80211_init_rate_ctrl_alg(local, NULL); if (result < 0) { @@ -1346,8 +1345,22 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) * because the driver cannot be handing us frames any * more and the tasklet is killed. */ - list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) + + /* + * First, we remove all non-master interfaces. Do this because they + * may have bss pointer dependency on the master, and when we free + * the master these would be freed as well, breaking our list + * iteration completely. + */ + list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) { + if (sdata->dev == local->mdev) + continue; + list_del(&sdata->list); __ieee80211_if_del(local, sdata); + } + + /* then, finally, remove the master interface */ + __ieee80211_if_del(local, IEEE80211_DEV_TO_SUB_IF(local->mdev)); rtnl_unlock(); diff --git a/net/mac80211/ieee80211_iface.c b/net/mac80211/ieee80211_iface.c index 2ba24ef319da..369ee4f52a29 100644 --- a/net/mac80211/ieee80211_iface.c +++ b/net/mac80211/ieee80211_iface.c @@ -127,6 +127,12 @@ int ieee80211_if_add_mgmt(struct ieee80211_local *local) if (ret) goto fail; + /* + * Called even when register_netdevice fails, it would + * oops if assigned before initialising the rest. + */ + ndev->uninit = ieee80211_if_reinit; + ieee80211_debugfs_add_netdev(nsdata); if (local->open_count > 0) @@ -155,12 +161,27 @@ void ieee80211_if_set_type(struct net_device *dev, int type) struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); int oldtype = sdata->type; - dev->hard_start_xmit = ieee80211_subif_start_xmit; + /* + * We need to call this function on the master interface + * which already has a hard_start_xmit routine assigned + * which must not be changed. + */ + if (!dev->hard_start_xmit) + dev->hard_start_xmit = ieee80211_subif_start_xmit; + /* + * Called even when register_netdevice fails, it would + * oops if assigned before initialising the rest. + */ + dev->uninit = ieee80211_if_reinit; + + /* most have no BSS pointer */ + sdata->bss = NULL; sdata->type = type; + switch (type) { case IEEE80211_IF_TYPE_WDS: - sdata->bss = NULL; + /* nothing special */ break; case IEEE80211_IF_TYPE_VLAN: sdata->u.vlan.ap = NULL; @@ -213,6 +234,7 @@ void ieee80211_if_reinit(struct net_device *dev) struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct sta_info *sta; + struct sk_buff *skb; ASSERT_RTNL(); @@ -246,12 +268,9 @@ void ieee80211_if_reinit(struct net_device *dev) kfree(sdata->u.ap.beacon_tail); kfree(sdata->u.ap.generic_elem); - if (dev != local->mdev) { - struct sk_buff *skb; - while ((skb = skb_dequeue(&sdata->u.ap.ps_bc_buf))) { - local->total_ps_buffered--; - dev_kfree_skb(skb); - } + while ((skb = skb_dequeue(&sdata->u.ap.ps_bc_buf))) { + local->total_ps_buffered--; + dev_kfree_skb(skb); } break;