diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2008-02-20 17:59:33 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-02-29 15:41:32 -0500 |
commit | d46e144b65bf053b25d134ec9f52a38e63e04bb4 (patch) | |
tree | 95a3e4ea7660511375f71c54335045a5d1441b1a /net/mac80211 | |
parent | d97cf01576e1867d26b5c8de360380f815a1b7df (diff) |
mac80211: rework TX filtered frame code
This reworks the code for TX filtered frames, splitting it out to
a new function to handle those cases, making the clear instruction
a flag and renaming a few things to be easier to understand and
less Atheros hardware specific. Finally, it also makes the comments
explain more.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211')
-rw-r--r-- | net/mac80211/ieee80211.c | 121 | ||||
-rw-r--r-- | net/mac80211/sta_info.h | 6 | ||||
-rw-r--r-- | net/mac80211/tx.c | 12 |
3 files changed, 89 insertions, 50 deletions
diff --git a/net/mac80211/ieee80211.c b/net/mac80211/ieee80211.c index 7df14799e38b..a00858dbab18 100644 --- a/net/mac80211/ieee80211.c +++ b/net/mac80211/ieee80211.c | |||
@@ -1178,6 +1178,77 @@ no_key: | |||
1178 | } | 1178 | } |
1179 | } | 1179 | } |
1180 | 1180 | ||
1181 | static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, | ||
1182 | struct sta_info *sta, | ||
1183 | struct sk_buff *skb, | ||
1184 | struct ieee80211_tx_status *status) | ||
1185 | { | ||
1186 | sta->tx_filtered_count++; | ||
1187 | |||
1188 | /* | ||
1189 | * Clear the TX filter mask for this STA when sending the next | ||
1190 | * packet. If the STA went to power save mode, this will happen | ||
1191 | * happen when it wakes up for the next time. | ||
1192 | */ | ||
1193 | sta->flags |= WLAN_STA_CLEAR_PS_FILT; | ||
1194 | |||
1195 | /* | ||
1196 | * This code races in the following way: | ||
1197 | * | ||
1198 | * (1) STA sends frame indicating it will go to sleep and does so | ||
1199 | * (2) hardware/firmware adds STA to filter list, passes frame up | ||
1200 | * (3) hardware/firmware processes TX fifo and suppresses a frame | ||
1201 | * (4) we get TX status before having processed the frame and | ||
1202 | * knowing that the STA has gone to sleep. | ||
1203 | * | ||
1204 | * This is actually quite unlikely even when both those events are | ||
1205 | * processed from interrupts coming in quickly after one another or | ||
1206 | * even at the same time because we queue both TX status events and | ||
1207 | * RX frames to be processed by a tasklet and process them in the | ||
1208 | * same order that they were received or TX status last. Hence, there | ||
1209 | * is no race as long as the frame RX is processed before the next TX | ||
1210 | * status, which drivers can ensure, see below. | ||
1211 | * | ||
1212 | * Note that this can only happen if the hardware or firmware can | ||
1213 | * actually add STAs to the filter list, if this is done by the | ||
1214 | * driver in response to set_tim() (which will only reduce the race | ||
1215 | * this whole filtering tries to solve, not completely solve it) | ||
1216 | * this situation cannot happen. | ||
1217 | * | ||
1218 | * To completely solve this race drivers need to make sure that they | ||
1219 | * (a) don't mix the irq-safe/not irq-safe TX status/RX processing | ||
1220 | * functions and | ||
1221 | * (b) always process RX events before TX status events if ordering | ||
1222 | * can be unknown, for example with different interrupt status | ||
1223 | * bits. | ||
1224 | */ | ||
1225 | if (sta->flags & WLAN_STA_PS && | ||
1226 | skb_queue_len(&sta->tx_filtered) < STA_MAX_TX_BUFFER) { | ||
1227 | ieee80211_remove_tx_extra(local, sta->key, skb, | ||
1228 | &status->control); | ||
1229 | skb_queue_tail(&sta->tx_filtered, skb); | ||
1230 | return; | ||
1231 | } | ||
1232 | |||
1233 | if (!(sta->flags & WLAN_STA_PS) && | ||
1234 | !(status->control.flags & IEEE80211_TXCTL_REQUEUE)) { | ||
1235 | /* Software retry the packet once */ | ||
1236 | status->control.flags |= IEEE80211_TXCTL_REQUEUE; | ||
1237 | ieee80211_remove_tx_extra(local, sta->key, skb, | ||
1238 | &status->control); | ||
1239 | dev_queue_xmit(skb); | ||
1240 | return; | ||
1241 | } | ||
1242 | |||
1243 | if (net_ratelimit()) | ||
1244 | printk(KERN_DEBUG "%s: dropped TX filtered frame, " | ||
1245 | "queue_len=%d PS=%d @%lu\n", | ||
1246 | wiphy_name(local->hw.wiphy), | ||
1247 | skb_queue_len(&sta->tx_filtered), | ||
1248 | !!(sta->flags & WLAN_STA_PS), jiffies); | ||
1249 | dev_kfree_skb(skb); | ||
1250 | } | ||
1251 | |||
1181 | void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb, | 1252 | void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb, |
1182 | struct ieee80211_tx_status *status) | 1253 | struct ieee80211_tx_status *status) |
1183 | { | 1254 | { |
@@ -1202,11 +1273,16 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb, | |||
1202 | sta = sta_info_get(local, hdr->addr1); | 1273 | sta = sta_info_get(local, hdr->addr1); |
1203 | if (sta) { | 1274 | if (sta) { |
1204 | if (sta->flags & WLAN_STA_PS) { | 1275 | if (sta->flags & WLAN_STA_PS) { |
1205 | /* The STA is in power save mode, so assume | 1276 | /* |
1277 | * The STA is in power save mode, so assume | ||
1206 | * that this TX packet failed because of that. | 1278 | * that this TX packet failed because of that. |
1207 | */ | 1279 | */ |
1208 | status->excessive_retries = 0; | 1280 | status->excessive_retries = 0; |
1209 | status->flags |= IEEE80211_TX_STATUS_TX_FILTERED; | 1281 | status->flags |= IEEE80211_TX_STATUS_TX_FILTERED; |
1282 | ieee80211_handle_filtered_frame(local, sta, | ||
1283 | skb, status); | ||
1284 | sta_info_put(sta); | ||
1285 | return; | ||
1210 | } | 1286 | } |
1211 | sta_info_put(sta); | 1287 | sta_info_put(sta); |
1212 | } | 1288 | } |
@@ -1216,47 +1292,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb, | |||
1216 | struct sta_info *sta; | 1292 | struct sta_info *sta; |
1217 | sta = sta_info_get(local, hdr->addr1); | 1293 | sta = sta_info_get(local, hdr->addr1); |
1218 | if (sta) { | 1294 | if (sta) { |
1219 | sta->tx_filtered_count++; | 1295 | ieee80211_handle_filtered_frame(local, sta, skb, |
1220 | 1296 | status); | |
1221 | /* Clear the TX filter mask for this STA when sending | ||
1222 | * the next packet. If the STA went to power save mode, | ||
1223 | * this will happen when it is waking up for the next | ||
1224 | * time. */ | ||
1225 | sta->clear_dst_mask = 1; | ||
1226 | |||
1227 | /* TODO: Is the WLAN_STA_PS flag always set here or is | ||
1228 | * the race between RX and TX status causing some | ||
1229 | * packets to be filtered out before 80211.o gets an | ||
1230 | * update for PS status? This seems to be the case, so | ||
1231 | * no changes are likely to be needed. */ | ||
1232 | if (sta->flags & WLAN_STA_PS && | ||
1233 | skb_queue_len(&sta->tx_filtered) < | ||
1234 | STA_MAX_TX_BUFFER) { | ||
1235 | ieee80211_remove_tx_extra(local, sta->key, | ||
1236 | skb, | ||
1237 | &status->control); | ||
1238 | skb_queue_tail(&sta->tx_filtered, skb); | ||
1239 | } else if (!(sta->flags & WLAN_STA_PS) && | ||
1240 | !(status->control.flags & IEEE80211_TXCTL_REQUEUE)) { | ||
1241 | /* Software retry the packet once */ | ||
1242 | status->control.flags |= IEEE80211_TXCTL_REQUEUE; | ||
1243 | ieee80211_remove_tx_extra(local, sta->key, | ||
1244 | skb, | ||
1245 | &status->control); | ||
1246 | dev_queue_xmit(skb); | ||
1247 | } else { | ||
1248 | if (net_ratelimit()) { | ||
1249 | printk(KERN_DEBUG "%s: dropped TX " | ||
1250 | "filtered frame queue_len=%d " | ||
1251 | "PS=%d @%lu\n", | ||
1252 | wiphy_name(local->hw.wiphy), | ||
1253 | skb_queue_len( | ||
1254 | &sta->tx_filtered), | ||
1255 | !!(sta->flags & WLAN_STA_PS), | ||
1256 | jiffies); | ||
1257 | } | ||
1258 | dev_kfree_skb(skb); | ||
1259 | } | ||
1260 | sta_info_put(sta); | 1297 | sta_info_put(sta); |
1261 | return; | 1298 | return; |
1262 | } | 1299 | } |
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 4099ece143ef..f7e65fa3f9ed 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h | |||
@@ -32,6 +32,9 @@ | |||
32 | * @WLAN_STA_WME: Station is a QoS-STA. | 32 | * @WLAN_STA_WME: Station is a QoS-STA. |
33 | * @WLAN_STA_WDS: Station is one of our WDS peers. | 33 | * @WLAN_STA_WDS: Station is one of our WDS peers. |
34 | * @WLAN_STA_PSPOLL: Station has just PS-polled us. | 34 | * @WLAN_STA_PSPOLL: Station has just PS-polled us. |
35 | * @WLAN_STA_CLEAR_PS_FILT: Clear PS filter in hardware (using the | ||
36 | * IEEE80211_TXCTL_CLEAR_PS_FILT control flag) when the next | ||
37 | * frame to this station is transmitted. | ||
35 | */ | 38 | */ |
36 | enum ieee80211_sta_info_flags { | 39 | enum ieee80211_sta_info_flags { |
37 | WLAN_STA_AUTH = 1<<0, | 40 | WLAN_STA_AUTH = 1<<0, |
@@ -43,6 +46,7 @@ enum ieee80211_sta_info_flags { | |||
43 | WLAN_STA_WME = 1<<6, | 46 | WLAN_STA_WME = 1<<6, |
44 | WLAN_STA_WDS = 1<<7, | 47 | WLAN_STA_WDS = 1<<7, |
45 | WLAN_STA_PSPOLL = 1<<8, | 48 | WLAN_STA_PSPOLL = 1<<8, |
49 | WLAN_STA_CLEAR_PS_FILT = 1<<9, | ||
46 | }; | 50 | }; |
47 | 51 | ||
48 | #define STA_TID_NUM 16 | 52 | #define STA_TID_NUM 16 |
@@ -136,8 +140,6 @@ struct sta_info { | |||
136 | struct sk_buff_head tx_filtered; /* buffer of TX frames that were | 140 | struct sk_buff_head tx_filtered; /* buffer of TX frames that were |
137 | * already given to low-level driver, | 141 | * already given to low-level driver, |
138 | * but were filtered */ | 142 | * but were filtered */ |
139 | int clear_dst_mask; | ||
140 | |||
141 | unsigned long rx_packets, tx_packets; /* number of RX/TX MSDUs */ | 143 | unsigned long rx_packets, tx_packets; /* number of RX/TX MSDUs */ |
142 | unsigned long rx_bytes, tx_bytes; | 144 | unsigned long rx_bytes, tx_bytes; |
143 | unsigned long tx_retry_failed, tx_retry_count; | 145 | unsigned long tx_retry_failed, tx_retry_count; |
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 69fdb763aa8b..1cd58e01f1ee 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c | |||
@@ -1020,10 +1020,10 @@ __ieee80211_tx_prepare(struct ieee80211_txrx_data *tx, | |||
1020 | } | 1020 | } |
1021 | 1021 | ||
1022 | if (!tx->sta) | 1022 | if (!tx->sta) |
1023 | control->flags |= IEEE80211_TXCTL_CLEAR_DST_MASK; | 1023 | control->flags |= IEEE80211_TXCTL_CLEAR_PS_FILT; |
1024 | else if (tx->sta->clear_dst_mask) { | 1024 | else if (tx->sta->flags & WLAN_STA_CLEAR_PS_FILT) { |
1025 | control->flags |= IEEE80211_TXCTL_CLEAR_DST_MASK; | 1025 | control->flags |= IEEE80211_TXCTL_CLEAR_PS_FILT; |
1026 | tx->sta->clear_dst_mask = 0; | 1026 | tx->sta->flags &= ~WLAN_STA_CLEAR_PS_FILT; |
1027 | } | 1027 | } |
1028 | 1028 | ||
1029 | hdrlen = ieee80211_get_hdrlen(tx->fc); | 1029 | hdrlen = ieee80211_get_hdrlen(tx->fc); |
@@ -1084,7 +1084,7 @@ static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb, | |||
1084 | if (tx->u.tx.extra_frag) { | 1084 | if (tx->u.tx.extra_frag) { |
1085 | control->flags &= ~(IEEE80211_TXCTL_USE_RTS_CTS | | 1085 | control->flags &= ~(IEEE80211_TXCTL_USE_RTS_CTS | |
1086 | IEEE80211_TXCTL_USE_CTS_PROTECT | | 1086 | IEEE80211_TXCTL_USE_CTS_PROTECT | |
1087 | IEEE80211_TXCTL_CLEAR_DST_MASK | | 1087 | IEEE80211_TXCTL_CLEAR_PS_FILT | |
1088 | IEEE80211_TXCTL_FIRST_FRAGMENT); | 1088 | IEEE80211_TXCTL_FIRST_FRAGMENT); |
1089 | for (i = 0; i < tx->u.tx.num_extra_frag; i++) { | 1089 | for (i = 0; i < tx->u.tx.num_extra_frag; i++) { |
1090 | if (!tx->u.tx.extra_frag[i]) | 1090 | if (!tx->u.tx.extra_frag[i]) |
@@ -1806,7 +1806,7 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, | |||
1806 | control->antenna_sel_tx = local->hw.conf.antenna_sel_tx; | 1806 | control->antenna_sel_tx = local->hw.conf.antenna_sel_tx; |
1807 | control->flags |= IEEE80211_TXCTL_NO_ACK; | 1807 | control->flags |= IEEE80211_TXCTL_NO_ACK; |
1808 | control->retry_limit = 1; | 1808 | control->retry_limit = 1; |
1809 | control->flags |= IEEE80211_TXCTL_CLEAR_DST_MASK; | 1809 | control->flags |= IEEE80211_TXCTL_CLEAR_PS_FILT; |
1810 | } | 1810 | } |
1811 | 1811 | ||
1812 | ap->num_beacons++; | 1812 | ap->num_beacons++; |