diff options
-rw-r--r-- | net/mac80211/ieee80211_i.h | 2 | ||||
-rw-r--r-- | net/mac80211/ieee80211_sta.c | 2 | ||||
-rw-r--r-- | net/mac80211/key.c | 9 | ||||
-rw-r--r-- | net/mac80211/sta_info.c | 70 | ||||
-rw-r--r-- | net/mac80211/sta_info.h | 2 |
5 files changed, 84 insertions, 1 deletions
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 7ab806602183..0997a0f96203 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -606,6 +606,8 @@ struct ieee80211_local { | |||
606 | spinlock_t sta_lock; | 606 | spinlock_t sta_lock; |
607 | unsigned long num_sta; | 607 | unsigned long num_sta; |
608 | struct list_head sta_list; | 608 | struct list_head sta_list; |
609 | struct list_head sta_flush_list; | ||
610 | struct work_struct sta_flush_work; | ||
609 | struct sta_info *sta_hash[STA_HASH_SIZE]; | 611 | struct sta_info *sta_hash[STA_HASH_SIZE]; |
610 | struct timer_list sta_cleanup; | 612 | struct timer_list sta_cleanup; |
611 | 613 | ||
diff --git a/net/mac80211/ieee80211_sta.c b/net/mac80211/ieee80211_sta.c index c5a47f8d873a..75b96a754333 100644 --- a/net/mac80211/ieee80211_sta.c +++ b/net/mac80211/ieee80211_sta.c | |||
@@ -2254,7 +2254,7 @@ static int ieee80211_sta_join_ibss(struct net_device *dev, | |||
2254 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | 2254 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
2255 | 2255 | ||
2256 | /* Remove possible STA entries from other IBSS networks. */ | 2256 | /* Remove possible STA entries from other IBSS networks. */ |
2257 | sta_info_flush(local, sdata); | 2257 | sta_info_flush_delayed(sdata); |
2258 | 2258 | ||
2259 | if (local->ops->reset_tsf) { | 2259 | if (local->ops->reset_tsf) { |
2260 | /* Reset own TSF to allow time synchronization work. */ | 2260 | /* Reset own TSF to allow time synchronization work. */ |
diff --git a/net/mac80211/key.c b/net/mac80211/key.c index f91fb4092652..5df9e0cc009f 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c | |||
@@ -73,6 +73,15 @@ static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key) | |||
73 | if (!key->local->ops->set_key) | 73 | if (!key->local->ops->set_key) |
74 | return; | 74 | return; |
75 | 75 | ||
76 | /* | ||
77 | * This makes sure that all pending flushes have | ||
78 | * actually completed prior to uploading new key | ||
79 | * material to the hardware. That is necessary to | ||
80 | * avoid races between flushing STAs and adding | ||
81 | * new keys for them. | ||
82 | */ | ||
83 | __ieee80211_run_pending_flush(key->local); | ||
84 | |||
76 | addr = get_mac_for_key(key); | 85 | addr = get_mac_for_key(key); |
77 | 86 | ||
78 | ret = key->local->ops->set_key(local_to_hw(key->local), SET_KEY, | 87 | ret = key->local->ops->set_key(local_to_hw(key->local), SET_KEY, |
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index dfca96e05d69..f5c65e891288 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c | |||
@@ -644,10 +644,41 @@ static void sta_info_debugfs_add_work(struct work_struct *work) | |||
644 | } | 644 | } |
645 | #endif | 645 | #endif |
646 | 646 | ||
647 | void __ieee80211_run_pending_flush(struct ieee80211_local *local) | ||
648 | { | ||
649 | struct sta_info *sta; | ||
650 | unsigned long flags; | ||
651 | |||
652 | ASSERT_RTNL(); | ||
653 | |||
654 | spin_lock_irqsave(&local->sta_lock, flags); | ||
655 | while (!list_empty(&local->sta_flush_list)) { | ||
656 | sta = list_first_entry(&local->sta_flush_list, | ||
657 | struct sta_info, list); | ||
658 | list_del(&sta->list); | ||
659 | spin_unlock_irqrestore(&local->sta_lock, flags); | ||
660 | sta_info_destroy(sta); | ||
661 | spin_lock_irqsave(&local->sta_lock, flags); | ||
662 | } | ||
663 | spin_unlock_irqrestore(&local->sta_lock, flags); | ||
664 | } | ||
665 | |||
666 | static void ieee80211_sta_flush_work(struct work_struct *work) | ||
667 | { | ||
668 | struct ieee80211_local *local = | ||
669 | container_of(work, struct ieee80211_local, sta_flush_work); | ||
670 | |||
671 | rtnl_lock(); | ||
672 | __ieee80211_run_pending_flush(local); | ||
673 | rtnl_unlock(); | ||
674 | } | ||
675 | |||
647 | void sta_info_init(struct ieee80211_local *local) | 676 | void sta_info_init(struct ieee80211_local *local) |
648 | { | 677 | { |
649 | spin_lock_init(&local->sta_lock); | 678 | spin_lock_init(&local->sta_lock); |
650 | INIT_LIST_HEAD(&local->sta_list); | 679 | INIT_LIST_HEAD(&local->sta_list); |
680 | INIT_LIST_HEAD(&local->sta_flush_list); | ||
681 | INIT_WORK(&local->sta_flush_work, ieee80211_sta_flush_work); | ||
651 | 682 | ||
652 | setup_timer(&local->sta_cleanup, sta_info_cleanup, | 683 | setup_timer(&local->sta_cleanup, sta_info_cleanup, |
653 | (unsigned long)local); | 684 | (unsigned long)local); |
@@ -668,7 +699,12 @@ int sta_info_start(struct ieee80211_local *local) | |||
668 | void sta_info_stop(struct ieee80211_local *local) | 699 | void sta_info_stop(struct ieee80211_local *local) |
669 | { | 700 | { |
670 | del_timer(&local->sta_cleanup); | 701 | del_timer(&local->sta_cleanup); |
702 | cancel_work_sync(&local->sta_flush_work); | ||
703 | |||
704 | rtnl_lock(); | ||
671 | sta_info_flush(local, NULL); | 705 | sta_info_flush(local, NULL); |
706 | __ieee80211_run_pending_flush(local); | ||
707 | rtnl_unlock(); | ||
672 | } | 708 | } |
673 | 709 | ||
674 | /** | 710 | /** |
@@ -688,6 +724,7 @@ int sta_info_flush(struct ieee80211_local *local, | |||
688 | unsigned long flags; | 724 | unsigned long flags; |
689 | 725 | ||
690 | might_sleep(); | 726 | might_sleep(); |
727 | ASSERT_RTNL(); | ||
691 | 728 | ||
692 | spin_lock_irqsave(&local->sta_lock, flags); | 729 | spin_lock_irqsave(&local->sta_lock, flags); |
693 | list_for_each_entry_safe(sta, tmp, &local->sta_list, list) { | 730 | list_for_each_entry_safe(sta, tmp, &local->sta_list, list) { |
@@ -706,3 +743,36 @@ int sta_info_flush(struct ieee80211_local *local, | |||
706 | 743 | ||
707 | return ret; | 744 | return ret; |
708 | } | 745 | } |
746 | |||
747 | /** | ||
748 | * sta_info_flush_delayed - flush matching STA entries from the STA table | ||
749 | * | ||
750 | * This function unlinks all stations for a given interface and queues | ||
751 | * them for freeing. Note that the workqueue function scheduled here has | ||
752 | * to run before any new keys can be added to the system to avoid set_key() | ||
753 | * callback ordering issues. | ||
754 | * | ||
755 | * @sdata: the interface | ||
756 | */ | ||
757 | void sta_info_flush_delayed(struct ieee80211_sub_if_data *sdata) | ||
758 | { | ||
759 | struct ieee80211_local *local = sdata->local; | ||
760 | struct sta_info *sta, *tmp; | ||
761 | unsigned long flags; | ||
762 | bool work = false; | ||
763 | |||
764 | spin_lock_irqsave(&local->sta_lock, flags); | ||
765 | list_for_each_entry_safe(sta, tmp, &local->sta_list, list) { | ||
766 | if (sdata == sta->sdata) { | ||
767 | __sta_info_unlink(&sta); | ||
768 | if (sta) { | ||
769 | list_add_tail(&sta->list, | ||
770 | &local->sta_flush_list); | ||
771 | work = true; | ||
772 | } | ||
773 | } | ||
774 | } | ||
775 | if (work) | ||
776 | schedule_work(&local->sta_flush_work); | ||
777 | spin_unlock_irqrestore(&local->sta_lock, flags); | ||
778 | } | ||
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 5e39a4164b9b..b09861eb124e 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h | |||
@@ -357,5 +357,7 @@ int sta_info_start(struct ieee80211_local *local); | |||
357 | void sta_info_stop(struct ieee80211_local *local); | 357 | void sta_info_stop(struct ieee80211_local *local); |
358 | int sta_info_flush(struct ieee80211_local *local, | 358 | int sta_info_flush(struct ieee80211_local *local, |
359 | struct ieee80211_sub_if_data *sdata); | 359 | struct ieee80211_sub_if_data *sdata); |
360 | void sta_info_flush_delayed(struct ieee80211_sub_if_data *sdata); | ||
361 | void __ieee80211_run_pending_flush(struct ieee80211_local *local); | ||
360 | 362 | ||
361 | #endif /* STA_INFO_H */ | 363 | #endif /* STA_INFO_H */ |