forked from Minki/linux
mac80211: add ieee80211_iterate_active_interfaces_rtnl()
If it is needed to disconnect multiple virtual interfaces after (WoWLAN-) suspend, the most obvious approach would be to iterate all interfaces by calling ieee80211_iterate_active_interfaces() and then call ieee80211_resume_disconnect() for each one. This is what the iwlmvm driver does. Unfortunately, this causes a locking dependency from mac80211's iflist_mtx to the key_mtx. This is problematic as the former is intentionally never held while calling any driver operation to allow drivers to iterate with their own locks held. The key_mtx is held while installing a key into the driver though, so this new lock dependency means drivers implementing the logic above can no longer hold their own lock while iterating. To fix this, add a new ieee80211_iterate_active_interfaces_rtnl() function that iterates while the RTNL is already held. This is true during suspend/resume, so that then the locking dependency isn't introduced. While at it, also refactor the various interface iterators and keep only a single implementation called by the various cases. Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
parent
272b98c645
commit
c7c71066c2
@ -3919,6 +3919,25 @@ void ieee80211_iterate_active_interfaces_atomic(struct ieee80211_hw *hw,
|
|||||||
struct ieee80211_vif *vif),
|
struct ieee80211_vif *vif),
|
||||||
void *data);
|
void *data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ieee80211_iterate_active_interfaces_rtnl - iterate active interfaces
|
||||||
|
*
|
||||||
|
* This function iterates over the interfaces associated with a given
|
||||||
|
* hardware that are currently active and calls the callback for them.
|
||||||
|
* This version can only be used while holding the RTNL.
|
||||||
|
*
|
||||||
|
* @hw: the hardware struct of which the interfaces should be iterated over
|
||||||
|
* @iter_flags: iteration flags, see &enum ieee80211_interface_iteration_flags
|
||||||
|
* @iterator: the iterator function to call, cannot sleep
|
||||||
|
* @data: first argument of the iterator function
|
||||||
|
*/
|
||||||
|
void ieee80211_iterate_active_interfaces_rtnl(struct ieee80211_hw *hw,
|
||||||
|
u32 iter_flags,
|
||||||
|
void (*iterator)(void *data,
|
||||||
|
u8 *mac,
|
||||||
|
struct ieee80211_vif *vif),
|
||||||
|
void *data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ieee80211_queue_work - add work onto the mac80211 workqueue
|
* ieee80211_queue_work - add work onto the mac80211 workqueue
|
||||||
*
|
*
|
||||||
|
@ -567,58 +567,14 @@ void ieee80211_flush_queues(struct ieee80211_local *local,
|
|||||||
IEEE80211_QUEUE_STOP_REASON_FLUSH);
|
IEEE80211_QUEUE_STOP_REASON_FLUSH);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ieee80211_iterate_active_interfaces(
|
static void __iterate_active_interfaces(struct ieee80211_local *local,
|
||||||
struct ieee80211_hw *hw, u32 iter_flags,
|
u32 iter_flags,
|
||||||
void (*iterator)(void *data, u8 *mac,
|
void (*iterator)(void *data, u8 *mac,
|
||||||
struct ieee80211_vif *vif),
|
struct ieee80211_vif *vif),
|
||||||
void *data)
|
void *data)
|
||||||
{
|
{
|
||||||
struct ieee80211_local *local = hw_to_local(hw);
|
|
||||||
struct ieee80211_sub_if_data *sdata;
|
struct ieee80211_sub_if_data *sdata;
|
||||||
|
|
||||||
mutex_lock(&local->iflist_mtx);
|
|
||||||
|
|
||||||
list_for_each_entry(sdata, &local->interfaces, list) {
|
|
||||||
switch (sdata->vif.type) {
|
|
||||||
case NL80211_IFTYPE_MONITOR:
|
|
||||||
if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))
|
|
||||||
continue;
|
|
||||||
break;
|
|
||||||
case NL80211_IFTYPE_AP_VLAN:
|
|
||||||
continue;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) &&
|
|
||||||
!(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
|
|
||||||
continue;
|
|
||||||
if (ieee80211_sdata_running(sdata))
|
|
||||||
iterator(data, sdata->vif.addr,
|
|
||||||
&sdata->vif);
|
|
||||||
}
|
|
||||||
|
|
||||||
sdata = rcu_dereference_protected(local->monitor_sdata,
|
|
||||||
lockdep_is_held(&local->iflist_mtx));
|
|
||||||
if (sdata &&
|
|
||||||
(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
|
|
||||||
sdata->flags & IEEE80211_SDATA_IN_DRIVER))
|
|
||||||
iterator(data, sdata->vif.addr, &sdata->vif);
|
|
||||||
|
|
||||||
mutex_unlock(&local->iflist_mtx);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces);
|
|
||||||
|
|
||||||
void ieee80211_iterate_active_interfaces_atomic(
|
|
||||||
struct ieee80211_hw *hw, u32 iter_flags,
|
|
||||||
void (*iterator)(void *data, u8 *mac,
|
|
||||||
struct ieee80211_vif *vif),
|
|
||||||
void *data)
|
|
||||||
{
|
|
||||||
struct ieee80211_local *local = hw_to_local(hw);
|
|
||||||
struct ieee80211_sub_if_data *sdata;
|
|
||||||
|
|
||||||
rcu_read_lock();
|
|
||||||
|
|
||||||
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
|
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
|
||||||
switch (sdata->vif.type) {
|
switch (sdata->vif.type) {
|
||||||
case NL80211_IFTYPE_MONITOR:
|
case NL80211_IFTYPE_MONITOR:
|
||||||
@ -638,16 +594,57 @@ void ieee80211_iterate_active_interfaces_atomic(
|
|||||||
&sdata->vif);
|
&sdata->vif);
|
||||||
}
|
}
|
||||||
|
|
||||||
sdata = rcu_dereference(local->monitor_sdata);
|
sdata = rcu_dereference_check(local->monitor_sdata,
|
||||||
|
lockdep_is_held(&local->iflist_mtx) ||
|
||||||
|
lockdep_rtnl_is_held());
|
||||||
if (sdata &&
|
if (sdata &&
|
||||||
(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
|
(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
|
||||||
sdata->flags & IEEE80211_SDATA_IN_DRIVER))
|
sdata->flags & IEEE80211_SDATA_IN_DRIVER))
|
||||||
iterator(data, sdata->vif.addr, &sdata->vif);
|
iterator(data, sdata->vif.addr, &sdata->vif);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ieee80211_iterate_active_interfaces(
|
||||||
|
struct ieee80211_hw *hw, u32 iter_flags,
|
||||||
|
void (*iterator)(void *data, u8 *mac,
|
||||||
|
struct ieee80211_vif *vif),
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
struct ieee80211_local *local = hw_to_local(hw);
|
||||||
|
|
||||||
|
mutex_lock(&local->iflist_mtx);
|
||||||
|
__iterate_active_interfaces(local, iter_flags, iterator, data);
|
||||||
|
mutex_unlock(&local->iflist_mtx);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces);
|
||||||
|
|
||||||
|
void ieee80211_iterate_active_interfaces_atomic(
|
||||||
|
struct ieee80211_hw *hw, u32 iter_flags,
|
||||||
|
void (*iterator)(void *data, u8 *mac,
|
||||||
|
struct ieee80211_vif *vif),
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
struct ieee80211_local *local = hw_to_local(hw);
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
__iterate_active_interfaces(local, iter_flags, iterator, data);
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic);
|
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic);
|
||||||
|
|
||||||
|
void ieee80211_iterate_active_interfaces_rtnl(
|
||||||
|
struct ieee80211_hw *hw, u32 iter_flags,
|
||||||
|
void (*iterator)(void *data, u8 *mac,
|
||||||
|
struct ieee80211_vif *vif),
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
struct ieee80211_local *local = hw_to_local(hw);
|
||||||
|
|
||||||
|
ASSERT_RTNL();
|
||||||
|
|
||||||
|
__iterate_active_interfaces(local, iter_flags, iterator, data);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Nothing should have been stuffed into the workqueue during
|
* Nothing should have been stuffed into the workqueue during
|
||||||
* the suspend->resume cycle. If this WARN is seen then there
|
* the suspend->resume cycle. If this WARN is seen then there
|
||||||
|
Loading…
Reference in New Issue
Block a user