diff options
author | Michael Wu <flamingice@sourmilk.net> | 2007-07-27 09:43:23 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-10-10 19:47:37 -0400 |
commit | be8755e1804d6f60e6a96a46ac6bc46ce6dfca53 (patch) | |
tree | a3038a924114f49aa2372ab7c9417d4e072c4b20 /net | |
parent | c2d1560ad8c2f6e0dd0d34102d022f3709325c26 (diff) |
[MAC80211]: improve locking of sta_info related structures
The sta_info code has some awkward locking which prevents some driver
callbacks from being allowed to sleep. This patch makes the locking more
focused so code that calls driver callbacks are allowed to sleep. It also
converts sta_lock to a rwlock.
Signed-off-by: Michael Wu <flamingice@sourmilk.net>
Signed-off-by: Jiri Benc <jbenc@suse.cz>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net')
-rw-r--r-- | net/mac80211/ieee80211.c | 6 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 11 | ||||
-rw-r--r-- | net/mac80211/ieee80211_iface.c | 2 | ||||
-rw-r--r-- | net/mac80211/ieee80211_sta.c | 20 | ||||
-rw-r--r-- | net/mac80211/sta_info.c | 184 | ||||
-rw-r--r-- | net/mac80211/sta_info.h | 11 | ||||
-rw-r--r-- | net/mac80211/tx.c | 8 |
7 files changed, 108 insertions, 134 deletions
diff --git a/net/mac80211/ieee80211.c b/net/mac80211/ieee80211.c index 1981058907b7..566bdca32b86 100644 --- a/net/mac80211/ieee80211.c +++ b/net/mac80211/ieee80211.c | |||
@@ -628,8 +628,8 @@ int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr) | |||
628 | /* Remove STA entry for the old peer */ | 628 | /* Remove STA entry for the old peer */ |
629 | sta = sta_info_get(local, sdata->u.wds.remote_addr); | 629 | sta = sta_info_get(local, sdata->u.wds.remote_addr); |
630 | if (sta) { | 630 | if (sta) { |
631 | sta_info_free(sta); | ||
631 | sta_info_put(sta); | 632 | sta_info_put(sta); |
632 | sta_info_free(sta, 0); | ||
633 | } else { | 633 | } else { |
634 | printk(KERN_DEBUG "%s: could not find STA entry for WDS link " | 634 | printk(KERN_DEBUG "%s: could not find STA entry for WDS link " |
635 | "peer " MAC_FMT "\n", | 635 | "peer " MAC_FMT "\n", |
@@ -776,13 +776,13 @@ static void ieee80211_stat_refresh(unsigned long data) | |||
776 | return; | 776 | return; |
777 | 777 | ||
778 | /* go through all stations */ | 778 | /* go through all stations */ |
779 | spin_lock_bh(&local->sta_lock); | 779 | read_lock_bh(&local->sta_lock); |
780 | list_for_each_entry(sta, &local->sta_list, list) { | 780 | list_for_each_entry(sta, &local->sta_list, list) { |
781 | sta->channel_use = (sta->channel_use_raw / local->stat_time) / | 781 | sta->channel_use = (sta->channel_use_raw / local->stat_time) / |
782 | CHAN_UTIL_PER_10MS; | 782 | CHAN_UTIL_PER_10MS; |
783 | sta->channel_use_raw = 0; | 783 | sta->channel_use_raw = 0; |
784 | } | 784 | } |
785 | spin_unlock_bh(&local->sta_lock); | 785 | read_unlock_bh(&local->sta_lock); |
786 | 786 | ||
787 | /* go through all subinterfaces */ | 787 | /* go through all subinterfaces */ |
788 | read_lock(&local->sub_if_lock); | 788 | read_lock(&local->sub_if_lock); |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index ed00552b3418..e76a58678959 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -417,10 +417,9 @@ struct ieee80211_local { | |||
417 | struct sk_buff_head skb_queue_unreliable; | 417 | struct sk_buff_head skb_queue_unreliable; |
418 | 418 | ||
419 | /* Station data structures */ | 419 | /* Station data structures */ |
420 | spinlock_t sta_lock; /* mutex for STA data structures */ | 420 | rwlock_t sta_lock; /* protects STA data structures */ |
421 | int num_sta; /* number of stations in sta_list */ | 421 | int num_sta; /* number of stations in sta_list */ |
422 | struct list_head sta_list; | 422 | struct list_head sta_list; |
423 | struct list_head deleted_sta_list; | ||
424 | struct sta_info *sta_hash[STA_HASH_SIZE]; | 423 | struct sta_info *sta_hash[STA_HASH_SIZE]; |
425 | struct timer_list sta_cleanup; | 424 | struct timer_list sta_cleanup; |
426 | 425 | ||
@@ -669,9 +668,9 @@ static inline void __bss_tim_set(struct ieee80211_if_ap *bss, int aid) | |||
669 | static inline void bss_tim_set(struct ieee80211_local *local, | 668 | static inline void bss_tim_set(struct ieee80211_local *local, |
670 | struct ieee80211_if_ap *bss, int aid) | 669 | struct ieee80211_if_ap *bss, int aid) |
671 | { | 670 | { |
672 | spin_lock_bh(&local->sta_lock); | 671 | read_lock_bh(&local->sta_lock); |
673 | __bss_tim_set(bss, aid); | 672 | __bss_tim_set(bss, aid); |
674 | spin_unlock_bh(&local->sta_lock); | 673 | read_unlock_bh(&local->sta_lock); |
675 | } | 674 | } |
676 | 675 | ||
677 | static inline void __bss_tim_clear(struct ieee80211_if_ap *bss, int aid) | 676 | static inline void __bss_tim_clear(struct ieee80211_if_ap *bss, int aid) |
@@ -686,9 +685,9 @@ static inline void __bss_tim_clear(struct ieee80211_if_ap *bss, int aid) | |||
686 | static inline void bss_tim_clear(struct ieee80211_local *local, | 685 | static inline void bss_tim_clear(struct ieee80211_local *local, |
687 | struct ieee80211_if_ap *bss, int aid) | 686 | struct ieee80211_if_ap *bss, int aid) |
688 | { | 687 | { |
689 | spin_lock_bh(&local->sta_lock); | 688 | read_lock_bh(&local->sta_lock); |
690 | __bss_tim_clear(bss, aid); | 689 | __bss_tim_clear(bss, aid); |
691 | spin_unlock_bh(&local->sta_lock); | 690 | read_unlock_bh(&local->sta_lock); |
692 | } | 691 | } |
693 | 692 | ||
694 | /** | 693 | /** |
diff --git a/net/mac80211/ieee80211_iface.c b/net/mac80211/ieee80211_iface.c index 8532a5ccdd1e..6db67767801d 100644 --- a/net/mac80211/ieee80211_iface.c +++ b/net/mac80211/ieee80211_iface.c | |||
@@ -272,8 +272,8 @@ void ieee80211_if_reinit(struct net_device *dev) | |||
272 | case IEEE80211_IF_TYPE_WDS: | 272 | case IEEE80211_IF_TYPE_WDS: |
273 | sta = sta_info_get(local, sdata->u.wds.remote_addr); | 273 | sta = sta_info_get(local, sdata->u.wds.remote_addr); |
274 | if (sta) { | 274 | if (sta) { |
275 | sta_info_free(sta); | ||
275 | sta_info_put(sta); | 276 | sta_info_put(sta); |
276 | sta_info_free(sta, 0); | ||
277 | } else { | 277 | } else { |
278 | #ifdef CONFIG_MAC80211_VERBOSE_DEBUG | 278 | #ifdef CONFIG_MAC80211_VERBOSE_DEBUG |
279 | printk(KERN_DEBUG "%s: Someone had deleted my STA " | 279 | printk(KERN_DEBUG "%s: Someone had deleted my STA " |
diff --git a/net/mac80211/ieee80211_sta.c b/net/mac80211/ieee80211_sta.c index 0d99b685df5f..9aee1abae127 100644 --- a/net/mac80211/ieee80211_sta.c +++ b/net/mac80211/ieee80211_sta.c | |||
@@ -773,7 +773,7 @@ static void ieee80211_associated(struct net_device *dev, | |||
773 | "range\n", | 773 | "range\n", |
774 | dev->name, MAC_ARG(ifsta->bssid)); | 774 | dev->name, MAC_ARG(ifsta->bssid)); |
775 | disassoc = 1; | 775 | disassoc = 1; |
776 | sta_info_free(sta, 0); | 776 | sta_info_free(sta); |
777 | ifsta->probereq_poll = 0; | 777 | ifsta->probereq_poll = 0; |
778 | } else { | 778 | } else { |
779 | ieee80211_send_probe_req(dev, ifsta->bssid, | 779 | ieee80211_send_probe_req(dev, ifsta->bssid, |
@@ -1890,7 +1890,7 @@ static int ieee80211_sta_active_ibss(struct net_device *dev) | |||
1890 | int active = 0; | 1890 | int active = 0; |
1891 | struct sta_info *sta; | 1891 | struct sta_info *sta; |
1892 | 1892 | ||
1893 | spin_lock_bh(&local->sta_lock); | 1893 | read_lock_bh(&local->sta_lock); |
1894 | list_for_each_entry(sta, &local->sta_list, list) { | 1894 | list_for_each_entry(sta, &local->sta_list, list) { |
1895 | if (sta->dev == dev && | 1895 | if (sta->dev == dev && |
1896 | time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL, | 1896 | time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL, |
@@ -1899,7 +1899,7 @@ static int ieee80211_sta_active_ibss(struct net_device *dev) | |||
1899 | break; | 1899 | break; |
1900 | } | 1900 | } |
1901 | } | 1901 | } |
1902 | spin_unlock_bh(&local->sta_lock); | 1902 | read_unlock_bh(&local->sta_lock); |
1903 | 1903 | ||
1904 | return active; | 1904 | return active; |
1905 | } | 1905 | } |
@@ -1909,16 +1909,24 @@ static void ieee80211_sta_expire(struct net_device *dev) | |||
1909 | { | 1909 | { |
1910 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | 1910 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); |
1911 | struct sta_info *sta, *tmp; | 1911 | struct sta_info *sta, *tmp; |
1912 | LIST_HEAD(tmp_list); | ||
1912 | 1913 | ||
1913 | spin_lock_bh(&local->sta_lock); | 1914 | write_lock_bh(&local->sta_lock); |
1914 | list_for_each_entry_safe(sta, tmp, &local->sta_list, list) | 1915 | list_for_each_entry_safe(sta, tmp, &local->sta_list, list) |
1915 | if (time_after(jiffies, sta->last_rx + | 1916 | if (time_after(jiffies, sta->last_rx + |
1916 | IEEE80211_IBSS_INACTIVITY_LIMIT)) { | 1917 | IEEE80211_IBSS_INACTIVITY_LIMIT)) { |
1917 | printk(KERN_DEBUG "%s: expiring inactive STA " MAC_FMT | 1918 | printk(KERN_DEBUG "%s: expiring inactive STA " MAC_FMT |
1918 | "\n", dev->name, MAC_ARG(sta->addr)); | 1919 | "\n", dev->name, MAC_ARG(sta->addr)); |
1919 | sta_info_free(sta, 1); | 1920 | __sta_info_get(sta); |
1921 | sta_info_remove(sta); | ||
1922 | list_add(&sta->list, &tmp_list); | ||
1920 | } | 1923 | } |
1921 | spin_unlock_bh(&local->sta_lock); | 1924 | write_unlock_bh(&local->sta_lock); |
1925 | |||
1926 | list_for_each_entry_safe(sta, tmp, &tmp_list, list) { | ||
1927 | sta_info_free(sta); | ||
1928 | sta_info_put(sta); | ||
1929 | } | ||
1922 | } | 1930 | } |
1923 | 1931 | ||
1924 | 1932 | ||
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index ab7b1f067c6e..34245b882c2b 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c | |||
@@ -32,38 +32,34 @@ static void sta_info_hash_add(struct ieee80211_local *local, | |||
32 | 32 | ||
33 | 33 | ||
34 | /* Caller must hold local->sta_lock */ | 34 | /* Caller must hold local->sta_lock */ |
35 | static void sta_info_hash_del(struct ieee80211_local *local, | 35 | static int sta_info_hash_del(struct ieee80211_local *local, |
36 | struct sta_info *sta) | 36 | struct sta_info *sta) |
37 | { | 37 | { |
38 | struct sta_info *s; | 38 | struct sta_info *s; |
39 | 39 | ||
40 | s = local->sta_hash[STA_HASH(sta->addr)]; | 40 | s = local->sta_hash[STA_HASH(sta->addr)]; |
41 | if (!s) | 41 | if (!s) |
42 | return; | 42 | return -ENOENT; |
43 | if (memcmp(s->addr, sta->addr, ETH_ALEN) == 0) { | 43 | if (s == sta) { |
44 | local->sta_hash[STA_HASH(sta->addr)] = s->hnext; | 44 | local->sta_hash[STA_HASH(sta->addr)] = s->hnext; |
45 | return; | 45 | return 0; |
46 | } | 46 | } |
47 | 47 | ||
48 | while (s->hnext && memcmp(s->hnext->addr, sta->addr, ETH_ALEN) != 0) | 48 | while (s->hnext && s->hnext != sta) |
49 | s = s->hnext; | 49 | s = s->hnext; |
50 | if (s->hnext) | 50 | if (s->hnext) { |
51 | s->hnext = s->hnext->hnext; | 51 | s->hnext = sta->hnext; |
52 | else | 52 | return 0; |
53 | printk(KERN_ERR "%s: could not remove STA " MAC_FMT " from " | 53 | } |
54 | "hash table\n", local->mdev->name, MAC_ARG(sta->addr)); | ||
55 | } | ||
56 | 54 | ||
57 | static inline void __sta_info_get(struct sta_info *sta) | 55 | return -ENOENT; |
58 | { | ||
59 | kref_get(&sta->kref); | ||
60 | } | 56 | } |
61 | 57 | ||
62 | struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr) | 58 | struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr) |
63 | { | 59 | { |
64 | struct sta_info *sta; | 60 | struct sta_info *sta; |
65 | 61 | ||
66 | spin_lock_bh(&local->sta_lock); | 62 | read_lock_bh(&local->sta_lock); |
67 | sta = local->sta_hash[STA_HASH(addr)]; | 63 | sta = local->sta_hash[STA_HASH(addr)]; |
68 | while (sta) { | 64 | while (sta) { |
69 | if (memcmp(sta->addr, addr, ETH_ALEN) == 0) { | 65 | if (memcmp(sta->addr, addr, ETH_ALEN) == 0) { |
@@ -72,7 +68,7 @@ struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr) | |||
72 | } | 68 | } |
73 | sta = sta->hnext; | 69 | sta = sta->hnext; |
74 | } | 70 | } |
75 | spin_unlock_bh(&local->sta_lock); | 71 | read_unlock_bh(&local->sta_lock); |
76 | 72 | ||
77 | return sta; | 73 | return sta; |
78 | } | 74 | } |
@@ -85,7 +81,7 @@ int sta_info_min_txrate_get(struct ieee80211_local *local) | |||
85 | int min_txrate = 9999999; | 81 | int min_txrate = 9999999; |
86 | int i; | 82 | int i; |
87 | 83 | ||
88 | spin_lock_bh(&local->sta_lock); | 84 | read_lock_bh(&local->sta_lock); |
89 | mode = local->oper_hw_mode; | 85 | mode = local->oper_hw_mode; |
90 | for (i = 0; i < STA_HASH_SIZE; i++) { | 86 | for (i = 0; i < STA_HASH_SIZE; i++) { |
91 | sta = local->sta_hash[i]; | 87 | sta = local->sta_hash[i]; |
@@ -95,7 +91,7 @@ int sta_info_min_txrate_get(struct ieee80211_local *local) | |||
95 | sta = sta->hnext; | 91 | sta = sta->hnext; |
96 | } | 92 | } |
97 | } | 93 | } |
98 | spin_unlock_bh(&local->sta_lock); | 94 | read_unlock_bh(&local->sta_lock); |
99 | if (min_txrate == 9999999) | 95 | if (min_txrate == 9999999) |
100 | min_txrate = 0; | 96 | min_txrate = 0; |
101 | 97 | ||
@@ -150,7 +146,6 @@ struct sta_info * sta_info_add(struct ieee80211_local *local, | |||
150 | sta->rate_ctrl_priv = rate_control_alloc_sta(sta->rate_ctrl, gfp); | 146 | sta->rate_ctrl_priv = rate_control_alloc_sta(sta->rate_ctrl, gfp); |
151 | if (!sta->rate_ctrl_priv) { | 147 | if (!sta->rate_ctrl_priv) { |
152 | rate_control_put(sta->rate_ctrl); | 148 | rate_control_put(sta->rate_ctrl); |
153 | kref_put(&sta->kref, sta_info_release); | ||
154 | kfree(sta); | 149 | kfree(sta); |
155 | return NULL; | 150 | return NULL; |
156 | } | 151 | } |
@@ -162,14 +157,14 @@ struct sta_info * sta_info_add(struct ieee80211_local *local, | |||
162 | skb_queue_head_init(&sta->tx_filtered); | 157 | skb_queue_head_init(&sta->tx_filtered); |
163 | __sta_info_get(sta); /* sta used by caller, decremented by | 158 | __sta_info_get(sta); /* sta used by caller, decremented by |
164 | * sta_info_put() */ | 159 | * sta_info_put() */ |
165 | spin_lock_bh(&local->sta_lock); | 160 | write_lock_bh(&local->sta_lock); |
166 | list_add(&sta->list, &local->sta_list); | 161 | list_add(&sta->list, &local->sta_list); |
167 | local->num_sta++; | 162 | local->num_sta++; |
168 | sta_info_hash_add(local, sta); | 163 | sta_info_hash_add(local, sta); |
169 | spin_unlock_bh(&local->sta_lock); | ||
170 | if (local->ops->sta_table_notification) | 164 | if (local->ops->sta_table_notification) |
171 | local->ops->sta_table_notification(local_to_hw(local), | 165 | local->ops->sta_table_notification(local_to_hw(local), |
172 | local->num_sta); | 166 | local->num_sta); |
167 | write_unlock_bh(&local->sta_lock); | ||
173 | sta->key_idx_compression = HW_KEY_IDX_INVALID; | 168 | sta->key_idx_compression = HW_KEY_IDX_INVALID; |
174 | 169 | ||
175 | #ifdef CONFIG_MAC80211_VERBOSE_DEBUG | 170 | #ifdef CONFIG_MAC80211_VERBOSE_DEBUG |
@@ -178,47 +173,25 @@ struct sta_info * sta_info_add(struct ieee80211_local *local, | |||
178 | #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ | 173 | #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ |
179 | 174 | ||
180 | #ifdef CONFIG_MAC80211_DEBUGFS | 175 | #ifdef CONFIG_MAC80211_DEBUGFS |
181 | if (!in_interrupt()) { | 176 | /* debugfs entry adding might sleep, so schedule process |
182 | sta->debugfs_registered = 1; | 177 | * context task for adding entry for STAs that do not yet |
183 | ieee80211_sta_debugfs_add(sta); | 178 | * have one. */ |
184 | rate_control_add_sta_debugfs(sta); | 179 | queue_work(local->hw.workqueue, &local->sta_debugfs_add); |
185 | } else { | ||
186 | /* debugfs entry adding might sleep, so schedule process | ||
187 | * context task for adding entry for STAs that do not yet | ||
188 | * have one. */ | ||
189 | queue_work(local->hw.workqueue, &local->sta_debugfs_add); | ||
190 | } | ||
191 | #endif | 180 | #endif |
192 | 181 | ||
193 | return sta; | 182 | return sta; |
194 | } | 183 | } |
195 | 184 | ||
196 | static void finish_sta_info_free(struct ieee80211_local *local, | 185 | /* Caller must hold local->sta_lock */ |
197 | struct sta_info *sta) | 186 | void sta_info_remove(struct sta_info *sta) |
198 | { | ||
199 | #ifdef CONFIG_MAC80211_VERBOSE_DEBUG | ||
200 | printk(KERN_DEBUG "%s: Removed STA " MAC_FMT "\n", | ||
201 | local->mdev->name, MAC_ARG(sta->addr)); | ||
202 | #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ | ||
203 | |||
204 | if (sta->key) { | ||
205 | ieee80211_debugfs_key_remove(sta->key); | ||
206 | ieee80211_key_free(sta->key); | ||
207 | sta->key = NULL; | ||
208 | } | ||
209 | |||
210 | rate_control_remove_sta_debugfs(sta); | ||
211 | ieee80211_sta_debugfs_remove(sta); | ||
212 | |||
213 | sta_info_put(sta); | ||
214 | } | ||
215 | |||
216 | static void sta_info_remove(struct sta_info *sta) | ||
217 | { | 187 | { |
218 | struct ieee80211_local *local = sta->local; | 188 | struct ieee80211_local *local = sta->local; |
219 | struct ieee80211_sub_if_data *sdata; | 189 | struct ieee80211_sub_if_data *sdata; |
220 | 190 | ||
221 | sta_info_hash_del(local, sta); | 191 | /* don't do anything if we've been removed already */ |
192 | if (sta_info_hash_del(local, sta)) | ||
193 | return; | ||
194 | |||
222 | list_del(&sta->list); | 195 | list_del(&sta->list); |
223 | sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); | 196 | sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); |
224 | if (sta->flags & WLAN_STA_PS) { | 197 | if (sta->flags & WLAN_STA_PS) { |
@@ -228,30 +201,29 @@ static void sta_info_remove(struct sta_info *sta) | |||
228 | } | 201 | } |
229 | local->num_sta--; | 202 | local->num_sta--; |
230 | sta_info_remove_aid_ptr(sta); | 203 | sta_info_remove_aid_ptr(sta); |
204 | |||
205 | if (local->ops->sta_table_notification) | ||
206 | local->ops->sta_table_notification(local_to_hw(local), | ||
207 | local->num_sta); | ||
231 | } | 208 | } |
232 | 209 | ||
233 | void sta_info_free(struct sta_info *sta, int locked) | 210 | void sta_info_free(struct sta_info *sta) |
234 | { | 211 | { |
235 | struct sk_buff *skb; | 212 | struct sk_buff *skb; |
236 | struct ieee80211_local *local = sta->local; | 213 | struct ieee80211_local *local = sta->local; |
237 | 214 | ||
238 | if (!locked) { | 215 | might_sleep(); |
239 | spin_lock_bh(&local->sta_lock); | 216 | |
240 | sta_info_remove(sta); | 217 | write_lock_bh(&local->sta_lock); |
241 | spin_unlock_bh(&local->sta_lock); | 218 | sta_info_remove(sta); |
242 | } else { | 219 | write_unlock_bh(&local->sta_lock); |
243 | sta_info_remove(sta); | ||
244 | } | ||
245 | if (local->ops->sta_table_notification) | ||
246 | local->ops->sta_table_notification(local_to_hw(local), | ||
247 | local->num_sta); | ||
248 | 220 | ||
249 | while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) { | 221 | while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) { |
250 | local->total_ps_buffered--; | 222 | local->total_ps_buffered--; |
251 | dev_kfree_skb_any(skb); | 223 | dev_kfree_skb(skb); |
252 | } | 224 | } |
253 | while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) { | 225 | while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) { |
254 | dev_kfree_skb_any(skb); | 226 | dev_kfree_skb(skb); |
255 | } | 227 | } |
256 | 228 | ||
257 | if (sta->key) { | 229 | if (sta->key) { |
@@ -276,13 +248,21 @@ void sta_info_free(struct sta_info *sta, int locked) | |||
276 | sta->key_idx_compression = HW_KEY_IDX_INVALID; | 248 | sta->key_idx_compression = HW_KEY_IDX_INVALID; |
277 | } | 249 | } |
278 | 250 | ||
279 | #ifdef CONFIG_MAC80211_DEBUGFS | 251 | #ifdef CONFIG_MAC80211_VERBOSE_DEBUG |
280 | if (in_atomic()) { | 252 | printk(KERN_DEBUG "%s: Removed STA " MAC_FMT "\n", |
281 | list_add(&sta->list, &local->deleted_sta_list); | 253 | local->mdev->name, MAC_ARG(sta->addr)); |
282 | queue_work(local->hw.workqueue, &local->sta_debugfs_add); | 254 | #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ |
283 | } else | 255 | |
284 | #endif | 256 | if (sta->key) { |
285 | finish_sta_info_free(local, sta); | 257 | ieee80211_debugfs_key_remove(sta->key); |
258 | ieee80211_key_free(sta->key); | ||
259 | sta->key = NULL; | ||
260 | } | ||
261 | |||
262 | rate_control_remove_sta_debugfs(sta); | ||
263 | ieee80211_sta_debugfs_remove(sta); | ||
264 | |||
265 | sta_info_put(sta); | ||
286 | } | 266 | } |
287 | 267 | ||
288 | 268 | ||
@@ -343,13 +323,13 @@ static void sta_info_cleanup(unsigned long data) | |||
343 | struct ieee80211_local *local = (struct ieee80211_local *) data; | 323 | struct ieee80211_local *local = (struct ieee80211_local *) data; |
344 | struct sta_info *sta; | 324 | struct sta_info *sta; |
345 | 325 | ||
346 | spin_lock_bh(&local->sta_lock); | 326 | read_lock_bh(&local->sta_lock); |
347 | list_for_each_entry(sta, &local->sta_list, list) { | 327 | list_for_each_entry(sta, &local->sta_list, list) { |
348 | __sta_info_get(sta); | 328 | __sta_info_get(sta); |
349 | sta_info_cleanup_expire_buffered(local, sta); | 329 | sta_info_cleanup_expire_buffered(local, sta); |
350 | sta_info_put(sta); | 330 | sta_info_put(sta); |
351 | } | 331 | } |
352 | spin_unlock_bh(&local->sta_lock); | 332 | read_unlock_bh(&local->sta_lock); |
353 | 333 | ||
354 | local->sta_cleanup.expires = jiffies + STA_INFO_CLEANUP_INTERVAL; | 334 | local->sta_cleanup.expires = jiffies + STA_INFO_CLEANUP_INTERVAL; |
355 | add_timer(&local->sta_cleanup); | 335 | add_timer(&local->sta_cleanup); |
@@ -363,35 +343,20 @@ static void sta_info_debugfs_add_task(struct work_struct *work) | |||
363 | struct sta_info *sta, *tmp; | 343 | struct sta_info *sta, *tmp; |
364 | 344 | ||
365 | while (1) { | 345 | while (1) { |
366 | spin_lock_bh(&local->sta_lock); | ||
367 | if (!list_empty(&local->deleted_sta_list)) { | ||
368 | sta = list_entry(local->deleted_sta_list.next, | ||
369 | struct sta_info, list); | ||
370 | list_del(local->deleted_sta_list.next); | ||
371 | } else | ||
372 | sta = NULL; | ||
373 | spin_unlock_bh(&local->sta_lock); | ||
374 | if (!sta) | ||
375 | break; | ||
376 | finish_sta_info_free(local, sta); | ||
377 | } | ||
378 | |||
379 | while (1) { | ||
380 | sta = NULL; | 346 | sta = NULL; |
381 | spin_lock_bh(&local->sta_lock); | 347 | read_lock_bh(&local->sta_lock); |
382 | list_for_each_entry(tmp, &local->sta_list, list) { | 348 | list_for_each_entry(tmp, &local->sta_list, list) { |
383 | if (!tmp->debugfs_registered) { | 349 | if (!tmp->debugfs.dir) { |
384 | sta = tmp; | 350 | sta = tmp; |
385 | __sta_info_get(sta); | 351 | __sta_info_get(sta); |
386 | break; | 352 | break; |
387 | } | 353 | } |
388 | } | 354 | } |
389 | spin_unlock_bh(&local->sta_lock); | 355 | read_unlock_bh(&local->sta_lock); |
390 | 356 | ||
391 | if (!sta) | 357 | if (!sta) |
392 | break; | 358 | break; |
393 | 359 | ||
394 | sta->debugfs_registered = 1; | ||
395 | ieee80211_sta_debugfs_add(sta); | 360 | ieee80211_sta_debugfs_add(sta); |
396 | rate_control_add_sta_debugfs(sta); | 361 | rate_control_add_sta_debugfs(sta); |
397 | sta_info_put(sta); | 362 | sta_info_put(sta); |
@@ -401,9 +366,8 @@ static void sta_info_debugfs_add_task(struct work_struct *work) | |||
401 | 366 | ||
402 | void sta_info_init(struct ieee80211_local *local) | 367 | void sta_info_init(struct ieee80211_local *local) |
403 | { | 368 | { |
404 | spin_lock_init(&local->sta_lock); | 369 | rwlock_init(&local->sta_lock); |
405 | INIT_LIST_HEAD(&local->sta_list); | 370 | INIT_LIST_HEAD(&local->sta_list); |
406 | INIT_LIST_HEAD(&local->deleted_sta_list); | ||
407 | 371 | ||
408 | init_timer(&local->sta_cleanup); | 372 | init_timer(&local->sta_cleanup); |
409 | local->sta_cleanup.expires = jiffies + STA_INFO_CLEANUP_INTERVAL; | 373 | local->sta_cleanup.expires = jiffies + STA_INFO_CLEANUP_INTERVAL; |
@@ -423,17 +387,8 @@ int sta_info_start(struct ieee80211_local *local) | |||
423 | 387 | ||
424 | void sta_info_stop(struct ieee80211_local *local) | 388 | void sta_info_stop(struct ieee80211_local *local) |
425 | { | 389 | { |
426 | struct sta_info *sta, *tmp; | ||
427 | |||
428 | del_timer(&local->sta_cleanup); | 390 | del_timer(&local->sta_cleanup); |
429 | 391 | sta_info_flush(local, NULL); | |
430 | list_for_each_entry_safe(sta, tmp, &local->sta_list, list) { | ||
431 | /* sta_info_free must be called with 0 as the last | ||
432 | * parameter to ensure all debugfs sta entries are | ||
433 | * unregistered. We don't need locking at this | ||
434 | * point. */ | ||
435 | sta_info_free(sta, 0); | ||
436 | } | ||
437 | } | 392 | } |
438 | 393 | ||
439 | void sta_info_remove_aid_ptr(struct sta_info *sta) | 394 | void sta_info_remove_aid_ptr(struct sta_info *sta) |
@@ -461,10 +416,19 @@ void sta_info_remove_aid_ptr(struct sta_info *sta) | |||
461 | void sta_info_flush(struct ieee80211_local *local, struct net_device *dev) | 416 | void sta_info_flush(struct ieee80211_local *local, struct net_device *dev) |
462 | { | 417 | { |
463 | struct sta_info *sta, *tmp; | 418 | struct sta_info *sta, *tmp; |
419 | LIST_HEAD(tmp_list); | ||
464 | 420 | ||
465 | spin_lock_bh(&local->sta_lock); | 421 | write_lock_bh(&local->sta_lock); |
466 | list_for_each_entry_safe(sta, tmp, &local->sta_list, list) | 422 | list_for_each_entry_safe(sta, tmp, &local->sta_list, list) |
467 | if (!dev || dev == sta->dev) | 423 | if (!dev || dev == sta->dev) { |
468 | sta_info_free(sta, 1); | 424 | __sta_info_get(sta); |
469 | spin_unlock_bh(&local->sta_lock); | 425 | sta_info_remove(sta); |
426 | list_add_tail(&sta->list, &tmp_list); | ||
427 | } | ||
428 | write_unlock_bh(&local->sta_lock); | ||
429 | |||
430 | list_for_each_entry_safe(sta, tmp, &tmp_list, list) { | ||
431 | sta_info_free(sta); | ||
432 | sta_info_put(sta); | ||
433 | } | ||
470 | } | 434 | } |
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index b5591d2f60a4..b5ef72379add 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h | |||
@@ -98,9 +98,6 @@ struct sta_info { | |||
98 | * filtering; used only if sta->key is not | 98 | * filtering; used only if sta->key is not |
99 | * set */ | 99 | * set */ |
100 | 100 | ||
101 | #ifdef CONFIG_MAC80211_DEBUGFS | ||
102 | int debugfs_registered; | ||
103 | #endif | ||
104 | int assoc_ap; /* whether this is an AP that we are | 101 | int assoc_ap; /* whether this is an AP that we are |
105 | * associated with as a client */ | 102 | * associated with as a client */ |
106 | 103 | ||
@@ -149,12 +146,18 @@ struct sta_info { | |||
149 | */ | 146 | */ |
150 | #define STA_INFO_CLEANUP_INTERVAL (10 * HZ) | 147 | #define STA_INFO_CLEANUP_INTERVAL (10 * HZ) |
151 | 148 | ||
149 | static inline void __sta_info_get(struct sta_info *sta) | ||
150 | { | ||
151 | kref_get(&sta->kref); | ||
152 | } | ||
153 | |||
152 | struct sta_info * sta_info_get(struct ieee80211_local *local, u8 *addr); | 154 | struct sta_info * sta_info_get(struct ieee80211_local *local, u8 *addr); |
153 | int sta_info_min_txrate_get(struct ieee80211_local *local); | 155 | int sta_info_min_txrate_get(struct ieee80211_local *local); |
154 | void sta_info_put(struct sta_info *sta); | 156 | void sta_info_put(struct sta_info *sta); |
155 | struct sta_info * sta_info_add(struct ieee80211_local *local, | 157 | struct sta_info * sta_info_add(struct ieee80211_local *local, |
156 | struct net_device *dev, u8 *addr, gfp_t gfp); | 158 | struct net_device *dev, u8 *addr, gfp_t gfp); |
157 | void sta_info_free(struct sta_info *sta, int locked); | 159 | void sta_info_remove(struct sta_info *sta); |
160 | void sta_info_free(struct sta_info *sta); | ||
158 | void sta_info_init(struct ieee80211_local *local); | 161 | void sta_info_init(struct ieee80211_local *local); |
159 | int sta_info_start(struct ieee80211_local *local); | 162 | int sta_info_start(struct ieee80211_local *local); |
160 | void sta_info_stop(struct ieee80211_local *local); | 163 | void sta_info_stop(struct ieee80211_local *local); |
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index dc128b412eab..2a1a7d457136 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c | |||
@@ -306,7 +306,7 @@ static void purge_old_ps_buffers(struct ieee80211_local *local) | |||
306 | } | 306 | } |
307 | read_unlock(&local->sub_if_lock); | 307 | read_unlock(&local->sub_if_lock); |
308 | 308 | ||
309 | spin_lock_bh(&local->sta_lock); | 309 | read_lock_bh(&local->sta_lock); |
310 | list_for_each_entry(sta, &local->sta_list, list) { | 310 | list_for_each_entry(sta, &local->sta_list, list) { |
311 | skb = skb_dequeue(&sta->ps_tx_buf); | 311 | skb = skb_dequeue(&sta->ps_tx_buf); |
312 | if (skb) { | 312 | if (skb) { |
@@ -315,7 +315,7 @@ static void purge_old_ps_buffers(struct ieee80211_local *local) | |||
315 | } | 315 | } |
316 | total += skb_queue_len(&sta->ps_tx_buf); | 316 | total += skb_queue_len(&sta->ps_tx_buf); |
317 | } | 317 | } |
318 | spin_unlock_bh(&local->sta_lock); | 318 | read_unlock_bh(&local->sta_lock); |
319 | 319 | ||
320 | local->total_ps_buffered = total; | 320 | local->total_ps_buffered = total; |
321 | printk(KERN_DEBUG "%s: PS buffers full - purged %d frames\n", | 321 | printk(KERN_DEBUG "%s: PS buffers full - purged %d frames\n", |
@@ -1629,7 +1629,7 @@ static void ieee80211_beacon_add_tim(struct ieee80211_local *local, | |||
1629 | 1629 | ||
1630 | /* Generate bitmap for TIM only if there are any STAs in power save | 1630 | /* Generate bitmap for TIM only if there are any STAs in power save |
1631 | * mode. */ | 1631 | * mode. */ |
1632 | spin_lock_bh(&local->sta_lock); | 1632 | read_lock_bh(&local->sta_lock); |
1633 | if (atomic_read(&bss->num_sta_ps) > 0) | 1633 | if (atomic_read(&bss->num_sta_ps) > 0) |
1634 | /* in the hope that this is faster than | 1634 | /* in the hope that this is faster than |
1635 | * checking byte-for-byte */ | 1635 | * checking byte-for-byte */ |
@@ -1680,7 +1680,7 @@ static void ieee80211_beacon_add_tim(struct ieee80211_local *local, | |||
1680 | *pos++ = aid0; /* Bitmap control */ | 1680 | *pos++ = aid0; /* Bitmap control */ |
1681 | *pos++ = 0; /* Part Virt Bitmap */ | 1681 | *pos++ = 0; /* Part Virt Bitmap */ |
1682 | } | 1682 | } |
1683 | spin_unlock_bh(&local->sta_lock); | 1683 | read_unlock_bh(&local->sta_lock); |
1684 | } | 1684 | } |
1685 | 1685 | ||
1686 | struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, int if_id, | 1686 | struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, int if_id, |