Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mac80211: use call_rcu() on sta deletion

mac80211 calls synchronize_rcu() on sta deletion,
which increase the roaming time significantly.

Convert it into a call_rcu() mechanism, in order
to avoid blocking. Since some of the cleanup
functions might sleep, schedule from the call_rcu
callback a new work that will do the actual cleanup.

In order to make sure the cleanup occurs before
the interface went down, flush local->workqueue
on ieee80211_do_stop().

Signed-off-by: Yoni Divinsky <yoni.divinsky@ti.com>
Signed-off-by: Eliad Peller <eliad@wizery.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>

authored by

Eliad Peller and committed by
Johannes Berg
b22cfcfc e548c49e

+81 -57
+12 -3
net/mac80211/iface.c
··· 793 793 flush_work(&sdata->work); 794 794 /* 795 795 * When we get here, the interface is marked down. 796 - * Call synchronize_rcu() to wait for the RX path 796 + * Call rcu_barrier() to wait both for the RX path 797 797 * should it be using the interface and enqueuing 798 - * frames at this very time on another CPU. 798 + * frames at this very time on another CPU, and 799 + * for the sta free call_rcu callbacks. 799 800 */ 800 - synchronize_rcu(); 801 + rcu_barrier(); 802 + 803 + /* 804 + * free_sta_rcu() enqueues a work for the actual 805 + * sta cleanup, so we need to flush it while 806 + * sdata is still valid. 807 + */ 808 + flush_workqueue(local->workqueue); 809 + 801 810 skb_queue_purge(&sdata->skb_queue); 802 811 803 812 /*
+67 -54
net/mac80211/sta_info.c
··· 91 91 return -ENOENT; 92 92 } 93 93 94 + static void free_sta_work(struct work_struct *wk) 95 + { 96 + struct sta_info *sta = container_of(wk, struct sta_info, free_sta_wk); 97 + int ac, i; 98 + struct tid_ampdu_tx *tid_tx; 99 + struct ieee80211_sub_if_data *sdata = sta->sdata; 100 + struct ieee80211_local *local = sdata->local; 101 + 102 + /* 103 + * At this point, when being called as call_rcu callback, 104 + * neither mac80211 nor the driver can reference this 105 + * sta struct any more except by still existing timers 106 + * associated with this station that we clean up below. 107 + */ 108 + 109 + if (test_sta_flag(sta, WLAN_STA_PS_STA)) { 110 + BUG_ON(!sdata->bss); 111 + 112 + clear_sta_flag(sta, WLAN_STA_PS_STA); 113 + 114 + atomic_dec(&sdata->bss->num_sta_ps); 115 + sta_info_recalc_tim(sta); 116 + } 117 + 118 + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { 119 + local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]); 120 + __skb_queue_purge(&sta->ps_tx_buf[ac]); 121 + __skb_queue_purge(&sta->tx_filtered[ac]); 122 + } 123 + 124 + #ifdef CONFIG_MAC80211_MESH 125 + if (ieee80211_vif_is_mesh(&sdata->vif)) { 126 + mesh_accept_plinks_update(sdata); 127 + mesh_plink_deactivate(sta); 128 + del_timer_sync(&sta->plink_timer); 129 + } 130 + #endif 131 + 132 + cancel_work_sync(&sta->drv_unblock_wk); 133 + 134 + /* 135 + * Destroy aggregation state here. It would be nice to wait for the 136 + * driver to finish aggregation stop and then clean up, but for now 137 + * drivers have to handle aggregation stop being requested, followed 138 + * directly by station destruction. 139 + */ 140 + for (i = 0; i < STA_TID_NUM; i++) { 141 + tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]); 142 + if (!tid_tx) 143 + continue; 144 + __skb_queue_purge(&tid_tx->pending); 145 + kfree(tid_tx); 146 + } 147 + 148 + sta_info_free(local, sta); 149 + } 150 + 151 + static void free_sta_rcu(struct rcu_head *h) 152 + { 153 + struct sta_info *sta = container_of(h, struct sta_info, rcu_head); 154 + 155 + ieee80211_queue_work(&sta->local->hw, &sta->free_sta_wk); 156 + } 157 + 94 158 /* protected by RCU */ 95 159 struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata, 96 160 const u8 *addr) ··· 305 241 306 242 spin_lock_init(&sta->lock); 307 243 INIT_WORK(&sta->drv_unblock_wk, sta_unblock); 244 + INIT_WORK(&sta->free_sta_wk, free_sta_work); 308 245 INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work); 309 246 mutex_init(&sta->ampdu_mlme.mtx); 310 247 ··· 719 654 { 720 655 struct ieee80211_local *local; 721 656 struct ieee80211_sub_if_data *sdata; 722 - int ret, i, ac; 723 - struct tid_ampdu_tx *tid_tx; 657 + int ret, i; 724 658 725 659 might_sleep(); 726 660 ··· 775 711 WARN_ON_ONCE(ret != 0); 776 712 } 777 713 778 - /* 779 - * At this point, after we wait for an RCU grace period, 780 - * neither mac80211 nor the driver can reference this 781 - * sta struct any more except by still existing timers 782 - * associated with this station that we clean up below. 783 - */ 784 - synchronize_rcu(); 785 - 786 - if (test_sta_flag(sta, WLAN_STA_PS_STA)) { 787 - BUG_ON(!sdata->bss); 788 - 789 - clear_sta_flag(sta, WLAN_STA_PS_STA); 790 - 791 - atomic_dec(&sdata->bss->num_sta_ps); 792 - sta_info_recalc_tim(sta); 793 - } 794 - 795 - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { 796 - local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]); 797 - __skb_queue_purge(&sta->ps_tx_buf[ac]); 798 - __skb_queue_purge(&sta->tx_filtered[ac]); 799 - } 800 - 801 - #ifdef CONFIG_MAC80211_MESH 802 - if (ieee80211_vif_is_mesh(&sdata->vif)) 803 - mesh_accept_plinks_update(sdata); 804 - #endif 805 - 806 714 sta_dbg(sdata, "Removed STA %pM\n", sta->sta.addr); 807 - 808 - cancel_work_sync(&sta->drv_unblock_wk); 809 715 810 716 cfg80211_del_sta(sdata->dev, sta->sta.addr, GFP_KERNEL); 811 717 812 718 rate_control_remove_sta_debugfs(sta); 813 719 ieee80211_sta_debugfs_remove(sta); 814 720 815 - #ifdef CONFIG_MAC80211_MESH 816 - if (ieee80211_vif_is_mesh(&sta->sdata->vif)) { 817 - mesh_plink_deactivate(sta); 818 - del_timer_sync(&sta->plink_timer); 819 - } 820 - #endif 821 - 822 - /* 823 - * Destroy aggregation state here. It would be nice to wait for the 824 - * driver to finish aggregation stop and then clean up, but for now 825 - * drivers have to handle aggregation stop being requested, followed 826 - * directly by station destruction. 827 - */ 828 - for (i = 0; i < STA_TID_NUM; i++) { 829 - tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]); 830 - if (!tid_tx) 831 - continue; 832 - __skb_queue_purge(&tid_tx->pending); 833 - kfree(tid_tx); 834 - } 835 - 836 - sta_info_free(local, sta); 721 + call_rcu(&sta->rcu_head, free_sta_rcu); 837 722 838 723 return 0; 839 724 }
+2
net/mac80211/sta_info.h
··· 287 287 struct sta_info { 288 288 /* General information, mostly static */ 289 289 struct list_head list; 290 + struct rcu_head rcu_head; 290 291 struct sta_info __rcu *hnext; 291 292 struct ieee80211_local *local; 292 293 struct ieee80211_sub_if_data *sdata; ··· 298 297 spinlock_t lock; 299 298 300 299 struct work_struct drv_unblock_wk; 300 + struct work_struct free_sta_wk; 301 301 302 302 u16 listen_interval; 303 303