diff options
author | Johannes Berg <johannes.berg@intel.com> | 2013-08-21 16:07:20 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2013-09-26 07:21:37 -0400 |
commit | c7c71066c27f2bafb2ce3b10c407c0285f56acfa (patch) | |
tree | 183c3557d596a8ae69bf3ae9219e7ef49456052e | |
parent | 272b98c6455f00884f0350f775c5342358ebb73f (diff) |
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>
-rw-r--r-- | include/net/mac80211.h | 19 | ||||
-rw-r--r-- | net/mac80211/util.c | 71 |
2 files changed, 53 insertions, 37 deletions
diff --git a/include/net/mac80211.h b/include/net/mac80211.h index cc6035f1a2f1..3411c59b636b 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h | |||
@@ -3920,6 +3920,25 @@ void ieee80211_iterate_active_interfaces_atomic(struct ieee80211_hw *hw, | |||
3920 | void *data); | 3920 | void *data); |
3921 | 3921 | ||
3922 | /** | 3922 | /** |
3923 | * ieee80211_iterate_active_interfaces_rtnl - iterate active interfaces | ||
3924 | * | ||
3925 | * This function iterates over the interfaces associated with a given | ||
3926 | * hardware that are currently active and calls the callback for them. | ||
3927 | * This version can only be used while holding the RTNL. | ||
3928 | * | ||
3929 | * @hw: the hardware struct of which the interfaces should be iterated over | ||
3930 | * @iter_flags: iteration flags, see &enum ieee80211_interface_iteration_flags | ||
3931 | * @iterator: the iterator function to call, cannot sleep | ||
3932 | * @data: first argument of the iterator function | ||
3933 | */ | ||
3934 | void ieee80211_iterate_active_interfaces_rtnl(struct ieee80211_hw *hw, | ||
3935 | u32 iter_flags, | ||
3936 | void (*iterator)(void *data, | ||
3937 | u8 *mac, | ||
3938 | struct ieee80211_vif *vif), | ||
3939 | void *data); | ||
3940 | |||
3941 | /** | ||
3923 | * ieee80211_queue_work - add work onto the mac80211 workqueue | 3942 | * ieee80211_queue_work - add work onto the mac80211 workqueue |
3924 | * | 3943 | * |
3925 | * Drivers and mac80211 use this to add work onto the mac80211 workqueue. | 3944 | * Drivers and mac80211 use this to add work onto the mac80211 workqueue. |
diff --git a/net/mac80211/util.c b/net/mac80211/util.c index e1b34a18b243..75a16853fb45 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c | |||
@@ -567,18 +567,15 @@ void ieee80211_flush_queues(struct ieee80211_local *local, | |||
567 | IEEE80211_QUEUE_STOP_REASON_FLUSH); | 567 | IEEE80211_QUEUE_STOP_REASON_FLUSH); |
568 | } | 568 | } |
569 | 569 | ||
570 | void ieee80211_iterate_active_interfaces( | 570 | static void __iterate_active_interfaces(struct ieee80211_local *local, |
571 | struct ieee80211_hw *hw, u32 iter_flags, | 571 | u32 iter_flags, |
572 | void (*iterator)(void *data, u8 *mac, | 572 | void (*iterator)(void *data, u8 *mac, |
573 | struct ieee80211_vif *vif), | 573 | struct ieee80211_vif *vif), |
574 | void *data) | 574 | void *data) |
575 | { | 575 | { |
576 | struct ieee80211_local *local = hw_to_local(hw); | ||
577 | struct ieee80211_sub_if_data *sdata; | 576 | struct ieee80211_sub_if_data *sdata; |
578 | 577 | ||
579 | mutex_lock(&local->iflist_mtx); | 578 | list_for_each_entry_rcu(sdata, &local->interfaces, list) { |
580 | |||
581 | list_for_each_entry(sdata, &local->interfaces, list) { | ||
582 | switch (sdata->vif.type) { | 579 | switch (sdata->vif.type) { |
583 | case NL80211_IFTYPE_MONITOR: | 580 | case NL80211_IFTYPE_MONITOR: |
584 | if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE)) | 581 | if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE)) |
@@ -597,13 +594,25 @@ void ieee80211_iterate_active_interfaces( | |||
597 | &sdata->vif); | 594 | &sdata->vif); |
598 | } | 595 | } |
599 | 596 | ||
600 | sdata = rcu_dereference_protected(local->monitor_sdata, | 597 | sdata = rcu_dereference_check(local->monitor_sdata, |
601 | lockdep_is_held(&local->iflist_mtx)); | 598 | lockdep_is_held(&local->iflist_mtx) || |
599 | lockdep_rtnl_is_held()); | ||
602 | if (sdata && | 600 | if (sdata && |
603 | (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || | 601 | (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || |
604 | sdata->flags & IEEE80211_SDATA_IN_DRIVER)) | 602 | sdata->flags & IEEE80211_SDATA_IN_DRIVER)) |
605 | iterator(data, sdata->vif.addr, &sdata->vif); | 603 | iterator(data, sdata->vif.addr, &sdata->vif); |
604 | } | ||
606 | 605 | ||
606 | void ieee80211_iterate_active_interfaces( | ||
607 | struct ieee80211_hw *hw, u32 iter_flags, | ||
608 | void (*iterator)(void *data, u8 *mac, | ||
609 | struct ieee80211_vif *vif), | ||
610 | void *data) | ||
611 | { | ||
612 | struct ieee80211_local *local = hw_to_local(hw); | ||
613 | |||
614 | mutex_lock(&local->iflist_mtx); | ||
615 | __iterate_active_interfaces(local, iter_flags, iterator, data); | ||
607 | mutex_unlock(&local->iflist_mtx); | 616 | mutex_unlock(&local->iflist_mtx); |
608 | } | 617 | } |
609 | EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces); | 618 | EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces); |
@@ -615,38 +624,26 @@ void ieee80211_iterate_active_interfaces_atomic( | |||
615 | void *data) | 624 | void *data) |
616 | { | 625 | { |
617 | struct ieee80211_local *local = hw_to_local(hw); | 626 | struct ieee80211_local *local = hw_to_local(hw); |
618 | struct ieee80211_sub_if_data *sdata; | ||
619 | 627 | ||
620 | rcu_read_lock(); | 628 | rcu_read_lock(); |
629 | __iterate_active_interfaces(local, iter_flags, iterator, data); | ||
630 | rcu_read_unlock(); | ||
631 | } | ||
632 | EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic); | ||
621 | 633 | ||
622 | list_for_each_entry_rcu(sdata, &local->interfaces, list) { | 634 | void ieee80211_iterate_active_interfaces_rtnl( |
623 | switch (sdata->vif.type) { | 635 | struct ieee80211_hw *hw, u32 iter_flags, |
624 | case NL80211_IFTYPE_MONITOR: | 636 | void (*iterator)(void *data, u8 *mac, |
625 | if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE)) | 637 | struct ieee80211_vif *vif), |
626 | continue; | 638 | void *data) |
627 | break; | 639 | { |
628 | case NL80211_IFTYPE_AP_VLAN: | 640 | struct ieee80211_local *local = hw_to_local(hw); |
629 | continue; | ||
630 | default: | ||
631 | break; | ||
632 | } | ||
633 | if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) && | ||
634 | !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) | ||
635 | continue; | ||
636 | if (ieee80211_sdata_running(sdata)) | ||
637 | iterator(data, sdata->vif.addr, | ||
638 | &sdata->vif); | ||
639 | } | ||
640 | 641 | ||
641 | sdata = rcu_dereference(local->monitor_sdata); | 642 | ASSERT_RTNL(); |
642 | if (sdata && | ||
643 | (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || | ||
644 | sdata->flags & IEEE80211_SDATA_IN_DRIVER)) | ||
645 | iterator(data, sdata->vif.addr, &sdata->vif); | ||
646 | 643 | ||
647 | rcu_read_unlock(); | 644 | __iterate_active_interfaces(local, iter_flags, iterator, data); |
648 | } | 645 | } |
649 | EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic); | 646 | EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl); |
650 | 647 | ||
651 | /* | 648 | /* |
652 | * Nothing should have been stuffed into the workqueue during | 649 | * Nothing should have been stuffed into the workqueue during |