From 925e64c3c512e9f4452eaa7d52fd4c1518b8fb11 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 16 May 2012 15:27:20 +0200 Subject: mac80211: run scan after finish connection monitoring commit 133d40f9a22bdfd2617a446f1e3209537c5415ec Author: Stanislaw Gruszka Date: Wed Mar 28 16:01:19 2012 +0200 mac80211: do not scan and monitor connection in parallel add bug, which make possible to start a scan and never finish it, so make every new scanning request finish with -EBUSY error. This can happen on code paths where we finish connection monitoring and clear IEEE80211_STA_*_POLL flags, but do not check if scan was deferred. This patch fixes those code paths. Signed-off-by: Stanislaw Gruszka Signed-off-by: John W. Linville --- net/mac80211/mlme.c | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 04c30630898..d94627c2929 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1220,6 +1220,22 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local, sdata->vif.bss_conf.qos = true; } +static void __ieee80211_stop_poll(struct ieee80211_sub_if_data *sdata) +{ + lockdep_assert_held(&sdata->local->mtx); + + sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL | + IEEE80211_STA_BEACON_POLL); + ieee80211_run_deferred_scan(sdata->local); +} + +static void ieee80211_stop_poll(struct ieee80211_sub_if_data *sdata) +{ + mutex_lock(&sdata->local->mtx); + __ieee80211_stop_poll(sdata); + mutex_unlock(&sdata->local->mtx); +} + static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata, u16 capab, bool erp_valid, u8 erp) { @@ -1285,8 +1301,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE; /* just to be sure */ - sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL | - IEEE80211_STA_BEACON_POLL); + ieee80211_stop_poll(sdata); ieee80211_led_assoc(local, 1); @@ -1456,8 +1471,7 @@ static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata) return; } - ifmgd->flags &= ~(IEEE80211_STA_CONNECTION_POLL | - IEEE80211_STA_BEACON_POLL); + __ieee80211_stop_poll(sdata); mutex_lock(&local->iflist_mtx); ieee80211_recalc_ps(local, -1); @@ -1477,7 +1491,6 @@ static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata) round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME)); out: - ieee80211_run_deferred_scan(local); mutex_unlock(&local->mtx); } @@ -2408,7 +2421,11 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, net_dbg_ratelimited("%s: cancelling probereq poll due to a received beacon\n", sdata->name); #endif + mutex_lock(&local->mtx); ifmgd->flags &= ~IEEE80211_STA_BEACON_POLL; + ieee80211_run_deferred_scan(local); + mutex_unlock(&local->mtx); + mutex_lock(&local->iflist_mtx); ieee80211_recalc_ps(local, -1); mutex_unlock(&local->iflist_mtx); @@ -2595,8 +2612,7 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; u8 frame_buf[DEAUTH_DISASSOC_LEN]; - ifmgd->flags &= ~(IEEE80211_STA_CONNECTION_POLL | - IEEE80211_STA_BEACON_POLL); + ieee80211_stop_poll(sdata); ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason, false, frame_buf); @@ -2874,8 +2890,7 @@ static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata) u32 flags; if (sdata->vif.type == NL80211_IFTYPE_STATION) { - sdata->u.mgd.flags &= ~(IEEE80211_STA_BEACON_POLL | - IEEE80211_STA_CONNECTION_POLL); + __ieee80211_stop_poll(sdata); /* let's probe the connection once */ flags = sdata->local->hw.flags; @@ -2944,7 +2959,10 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata) if (test_and_clear_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running)) add_timer(&ifmgd->chswitch_timer); ieee80211_sta_reset_beacon_monitor(sdata); + + mutex_lock(&sdata->local->mtx); ieee80211_restart_sta_timer(sdata); + mutex_unlock(&sdata->local->mtx); } #endif -- cgit v1.2.3 From 28f333666ea766fdfb25de3783ff56cd2d1c51f0 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Tue, 29 May 2012 15:39:06 -0700 Subject: cfg80211: use sme_state in ibss start/join path CFG80211_DEV_WARN_ON() at "net/wireless/ibss.c line 63" is unnecessarily triggered even after successful connection, when cfg80211_ibss_joined() is called by driver inside .join_ibss handler. This patch fixes the problem by changing 'sme_state' in ibss path and having WARN_ON() check for 'sme_state' similar to infra association. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- net/wireless/ibss.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index d2a19b0ff71..89baa332841 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -42,6 +42,7 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid) cfg80211_hold_bss(bss_from_pub(bss)); wdev->current_bss = bss_from_pub(bss); + wdev->sme_state = CFG80211_SME_CONNECTED; cfg80211_upload_connect_keys(wdev); nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid, @@ -60,7 +61,7 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp) struct cfg80211_event *ev; unsigned long flags; - CFG80211_DEV_WARN_ON(!wdev->ssid_len); + CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING); ev = kzalloc(sizeof(*ev), gfp); if (!ev) @@ -115,9 +116,11 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, #ifdef CONFIG_CFG80211_WEXT wdev->wext.ibss.channel = params->channel; #endif + wdev->sme_state = CFG80211_SME_CONNECTING; err = rdev->ops->join_ibss(&rdev->wiphy, dev, params); if (err) { wdev->connect_keys = NULL; + wdev->sme_state = CFG80211_SME_IDLE; return err; } @@ -169,6 +172,7 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext) } wdev->current_bss = NULL; + wdev->sme_state = CFG80211_SME_IDLE; wdev->ssid_len = 0; #ifdef CONFIG_CFG80211_WEXT if (!nowext) -- cgit v1.2.3 From b8bacc187aa5b59af9b9fa19b3ce4df5ad1db112 Mon Sep 17 00:00:00 2001 From: Chun-Yeow Yeoh Date: Wed, 30 May 2012 09:30:41 +0800 Subject: mac80211: Fix Unreachable Mesh Station Problem when joining to another MBSS Mesh station that joins an MBSS is reachable using mesh portal with 6 address frame by mesh stations from another MBSS if these two different MBSSes are bridged. However, if the mesh station later moves into the same MBSS of those mesh stations, it is unreachable by mesh stations in the MBSS due to the mpp_paths table is not deleted. A quick fix is to perform mesh_path_lookup, if it is available for the target destination, mpp_path_lookup is not performed. When the mesh station moves back to its original MBSS, the mesh_paths will be deleted once expired. So, it will be reachable using mpp_path_lookup again. Signed-off-by: Chun-Yeow Yeoh Signed-off-by: John W. Linville --- net/mac80211/tx.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 847215bb2a6..e453212fa17 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1737,7 +1737,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, __le16 fc; struct ieee80211_hdr hdr; struct ieee80211s_hdr mesh_hdr __maybe_unused; - struct mesh_path __maybe_unused *mppath = NULL; + struct mesh_path __maybe_unused *mppath = NULL, *mpath = NULL; const u8 *encaps_data; int encaps_len, skip_header_bytes; int nh_pos, h_pos; @@ -1803,8 +1803,11 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, goto fail; } rcu_read_lock(); - if (!is_multicast_ether_addr(skb->data)) - mppath = mpp_path_lookup(skb->data, sdata); + if (!is_multicast_ether_addr(skb->data)) { + mpath = mesh_path_lookup(skb->data, sdata); + if (!mpath) + mppath = mpp_path_lookup(skb->data, sdata); + } /* * Use address extension if it is a packet from -- cgit v1.2.3 From bd34ab62a3297bd7685da11b0cbe05ae4cd8b02c Mon Sep 17 00:00:00 2001 From: Meenakshi Venkataraman Date: Wed, 30 May 2012 11:39:33 +0200 Subject: mac80211: fix error in station state transitions during reconfig As part of hardware reconfig mac80211 tries to restore the station state to its values before the hardware reconfig, but it only goes to the last-state - 1. Fix this off-by-one error. Cc: stable@kernel.org [3.4] Signed-off-by: Meenakshi Venkataraman Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/util.c b/net/mac80211/util.c index a44c6807df0..8dd4712620f 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1271,7 +1271,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) enum ieee80211_sta_state state; for (state = IEEE80211_STA_NOTEXIST; - state < sta->sta_state - 1; state++) + state < sta->sta_state; state++) WARN_ON(drv_sta_state(local, sta->sdata, sta, state, state + 1)); } -- cgit v1.2.3 From 71ecfa1893034eeb1c93e02e22ee2ad26d080858 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 31 May 2012 15:09:27 +0200 Subject: mac80211: clean up remain-on-channel on interface stop When any interface goes down, it could be the one that we were doing a remain-on-channel with. We therefore need to cancel the remain-on-channel and flush the related work structs so they don't run after the interface has been removed or even destroyed. It's also possible in this case that an off-channel SKB was never transmitted, so free it if this is the case. Note that this can also happen if the driver finishes the off-channel period without ever starting it. Cc: stable@kernel.org Reported-by: Nirav Shah Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/iface.c | 12 ++++++++++++ net/mac80211/offchannel.c | 16 ++++++++++++++++ 2 files changed, 28 insertions(+) (limited to 'net') diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index d4c19a7773d..8664111d056 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -637,6 +637,18 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, ieee80211_configure_filter(local); break; default: + mutex_lock(&local->mtx); + if (local->hw_roc_dev == sdata->dev && + local->hw_roc_channel) { + /* ignore return value since this is racy */ + drv_cancel_remain_on_channel(local); + ieee80211_queue_work(&local->hw, &local->hw_roc_done); + } + mutex_unlock(&local->mtx); + + flush_work(&local->hw_roc_start); + flush_work(&local->hw_roc_done); + flush_work(&sdata->work); /* * When we get here, the interface is marked down. diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index f054e94901a..935aa4b6dee 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -234,6 +234,22 @@ static void ieee80211_hw_roc_done(struct work_struct *work) return; } + /* was never transmitted */ + if (local->hw_roc_skb) { + u64 cookie; + + cookie = local->hw_roc_cookie ^ 2; + + cfg80211_mgmt_tx_status(local->hw_roc_dev, cookie, + local->hw_roc_skb->data, + local->hw_roc_skb->len, false, + GFP_KERNEL); + + kfree_skb(local->hw_roc_skb); + local->hw_roc_skb = NULL; + local->hw_roc_skb_for_status = NULL; + } + if (!local->hw_roc_for_tx) cfg80211_remain_on_channel_expired(local->hw_roc_dev, local->hw_roc_cookie, -- cgit v1.2.3 From d8c7aae64cd2db5eccc631c29fa978a24fb1feef Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 30 May 2012 15:32:24 +0200 Subject: mac80211: add missing rcu_read_lock/unlock in agg-rx session timer Fixes a lockdep warning: =================================================== [ INFO: suspicious rcu_dereference_check() usage. ] --------------------------------------------------- net/mac80211/agg-rx.c:148 invoked rcu_dereference_check() without protection! other info that might help us debug this: rcu_scheduler_active = 1, debug_locks = 1 1 lock held by arecord/11226: #0: (&tid_agg_rx->session_timer){+.-...}, at: [] call_timer_fn+0x0/0x360 stack backtrace: Pid: 11226, comm: arecord Not tainted 3.1.0-kml #16 Call Trace: [] lockdep_rcu_dereference+0xa4/0xc0 [] sta_rx_agg_session_timer_expired+0xc9/0x110 [mac80211] [] ? ieee80211_process_addba_resp+0x220/0x220 [mac80211] [] call_timer_fn+0x8a/0x360 [] ? init_timer_deferrable_key+0x30/0x30 [] ? _raw_spin_unlock_irq+0x30/0x70 [] run_timer_softirq+0x139/0x310 [] ? put_lock_stats.isra.25+0xe/0x40 [] ? lock_release_holdtime.part.26+0xdc/0x160 [] ? ieee80211_process_addba_resp+0x220/0x220 [mac80211] [] __do_softirq+0xc8/0x3c0 [] ? tick_dev_program_event+0x48/0x110 [] ? tick_program_event+0x1f/0x30 [] ? putname+0x35/0x50 [] call_softirq+0x1c/0x30 [] do_softirq+0xa5/0xe0 [] irq_exit+0xae/0xe0 [] smp_apic_timer_interrupt+0x6b/0x98 [] apic_timer_interrupt+0x73/0x80 [] ? free_debug_processing+0x1a1/0x1d5 [] ? putname+0x35/0x50 [] __slab_free+0x31/0x2ca [] ? _raw_spin_unlock_irqrestore+0x4a/0x90 [] ? __debug_check_no_obj_freed+0x15f/0x210 [] ? lock_release_nested+0x84/0xc0 [] ? kmem_cache_free+0x105/0x250 [] ? putname+0x35/0x50 [] ? putname+0x35/0x50 [] kmem_cache_free+0x23f/0x250 [] putname+0x35/0x50 [] do_sys_open+0x16d/0x1d0 [] sys_open+0x20/0x30 [] system_call_fastpath+0x16/0x1b Reported-by: Johannes Berg Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- net/mac80211/agg-rx.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 26ddb699d69..c649188314c 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -145,15 +145,20 @@ static void sta_rx_agg_session_timer_expired(unsigned long data) struct tid_ampdu_rx *tid_rx; unsigned long timeout; + rcu_read_lock(); tid_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[*ptid]); - if (!tid_rx) + if (!tid_rx) { + rcu_read_unlock(); return; + } timeout = tid_rx->last_rx + TU_TO_JIFFIES(tid_rx->timeout); if (time_is_after_jiffies(timeout)) { mod_timer(&tid_rx->session_timer, timeout); + rcu_read_unlock(); return; } + rcu_read_unlock(); #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "rx session timer expired on tid %d\n", (u16)*ptid); -- cgit v1.2.3 From 5204267d2fd5e98fc52b44fec01ad10352642b78 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 30 May 2012 13:25:54 -0700 Subject: mac80211: Fix likely misuse of | for & Using | with a constant is always true. Likely this should have be &. cc: Ben Greear Signed-off-by: Joe Perches Signed-off-by: John W. Linville --- net/mac80211/cfg.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 495831ee48f..e9cecca5c44 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -533,16 +533,16 @@ static void ieee80211_get_et_stats(struct wiphy *wiphy, sinfo.filled = 0; sta_set_sinfo(sta, &sinfo); - if (sinfo.filled | STATION_INFO_TX_BITRATE) + if (sinfo.filled & STATION_INFO_TX_BITRATE) data[i] = 100000 * cfg80211_calculate_bitrate(&sinfo.txrate); i++; - if (sinfo.filled | STATION_INFO_RX_BITRATE) + if (sinfo.filled & STATION_INFO_RX_BITRATE) data[i] = 100000 * cfg80211_calculate_bitrate(&sinfo.rxrate); i++; - if (sinfo.filled | STATION_INFO_SIGNAL_AVG) + if (sinfo.filled & STATION_INFO_SIGNAL_AVG) data[i] = (u8)sinfo.signal_avg; i++; } else { -- cgit v1.2.3 From 794454ce72a298de6f4536ade597bdcc7dcde7c7 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sun, 3 Jun 2012 23:32:32 +0300 Subject: mac80211: fix non RCU-safe sta_list manipulation sta_info_cleanup locks the sta_list using rcu_read_lock however the delete operation isn't rcu safe. A race between sta_info_cleanup timer being called and a STA being removed can occur which leads to a panic while traversing sta_list. Fix this by switching to the RCU-safe versions. Cc: stable@vger.kernel.org Reported-by: Eyal Shapira Signed-off-by: Arik Nemtsov Signed-off-by: John W. Linville --- net/mac80211/sta_info.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index f5b1638fbf8..de455f8bbb9 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -378,7 +378,7 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU) /* make the station visible */ sta_info_hash_add(local, sta); - list_add(&sta->list, &local->sta_list); + list_add_rcu(&sta->list, &local->sta_list); set_sta_flag(sta, WLAN_STA_INSERTED); @@ -688,7 +688,7 @@ int __must_check __sta_info_destroy(struct sta_info *sta) if (ret) return ret; - list_del(&sta->list); + list_del_rcu(&sta->list); mutex_lock(&local->key_mtx); for (i = 0; i < NUM_DEFAULT_KEYS; i++) -- cgit v1.2.3 From ddcd0f41471a1e0394c8840a119ec3986a78462c Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Thu, 31 May 2012 22:53:39 -0300 Subject: Bluetooth: Fix checking the wrong flag when accepting a socket Most probably a typo, the check should have been for BT_SK_DEFER_SETUP instead of BT_DEFER_SETUP (which right now only represents a socket option). Signed-off-by: Vinicius Costa Gomes Acked-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/af_bluetooth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 46e7f86acfc..3e18af4dadc 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -210,7 +210,7 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock) } if (sk->sk_state == BT_CONNECTED || !newsock || - test_bit(BT_DEFER_SETUP, &bt_sk(parent)->flags)) { + test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags)) { bt_accept_unlink(sk); if (newsock) sock_graft(sk, newsock); -- cgit v1.2.3 From 463454b5dbd8dbab6e2fc6c557329e5b811b9c32 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 5 Jun 2012 12:16:50 +0200 Subject: cfg80211: fix interface combinations check If a given interface combination doesn't contain a required interface type then we missed checking that and erroneously allowed it even though iface type wasn't there at all. Add a check that makes sure that all interface types are accounted for. Cc: stable@kernel.org Reported-by: Mohammed Shafi Shajakhan Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/util.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/util.c b/net/wireless/util.c index 55d99466bab..8f2d68fc3a4 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -935,6 +935,7 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, enum nl80211_iftype iftype) { struct wireless_dev *wdev_iter; + u32 used_iftypes = BIT(iftype); int num[NUM_NL80211_IFTYPES]; int total = 1; int i, j; @@ -961,6 +962,7 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, num[wdev_iter->iftype]++; total++; + used_iftypes |= BIT(wdev_iter->iftype); } mutex_unlock(&rdev->devlist_mtx); @@ -970,6 +972,7 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, for (i = 0; i < rdev->wiphy.n_iface_combinations; i++) { const struct ieee80211_iface_combination *c; struct ieee80211_iface_limit *limits; + u32 all_iftypes = 0; c = &rdev->wiphy.iface_combinations[i]; @@ -984,6 +987,7 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, if (rdev->wiphy.software_iftypes & BIT(iftype)) continue; for (j = 0; j < c->n_limits; j++) { + all_iftypes |= limits[j].types; if (!(limits[j].types & BIT(iftype))) continue; if (limits[j].max < num[iftype]) @@ -991,7 +995,20 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, limits[j].max -= num[iftype]; } } - /* yay, it fits */ + + /* + * Finally check that all iftypes that we're currently + * using are actually part of this combination. If they + * aren't then we can't use this combination and have + * to continue to the next. + */ + if ((all_iftypes & used_iftypes) != used_iftypes) + goto cont; + + /* + * This combination covered all interface types and + * supported the requested numbers, so we're good. + */ kfree(limits); return 0; cont: -- cgit v1.2.3