aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2008-02-20 17:59:33 -0500
committerJohn W. Linville <linville@tuxdriver.com>2008-02-29 15:41:32 -0500
commitd46e144b65bf053b25d134ec9f52a38e63e04bb4 (patch)
tree95a3e4ea7660511375f71c54335045a5d1441b1a
parentd97cf01576e1867d26b5c8de360380f815a1b7df (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>
-rw-r--r--include/net/mac80211.h3
-rw-r--r--net/mac80211/ieee80211.c121
-rw-r--r--net/mac80211/sta_info.h6
-rw-r--r--net/mac80211/tx.c12
4 files changed, 91 insertions, 51 deletions
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 784ab769b00b..cd4b1c1a0d48 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -228,7 +228,8 @@ struct ieee80211_tx_control {
228#define IEEE80211_TXCTL_NO_ACK (1<<4) /* tell the low level not to 228#define IEEE80211_TXCTL_NO_ACK (1<<4) /* tell the low level not to
229 * wait for an ack */ 229 * wait for an ack */
230#define IEEE80211_TXCTL_RATE_CTRL_PROBE (1<<5) 230#define IEEE80211_TXCTL_RATE_CTRL_PROBE (1<<5)
231#define IEEE80211_TXCTL_CLEAR_DST_MASK (1<<6) 231#define IEEE80211_TXCTL_CLEAR_PS_FILT (1<<6) /* clear powersave filter
232 * for destination station */
232#define IEEE80211_TXCTL_REQUEUE (1<<7) 233#define IEEE80211_TXCTL_REQUEUE (1<<7)
233#define IEEE80211_TXCTL_FIRST_FRAGMENT (1<<8) /* this is a first fragment of 234#define IEEE80211_TXCTL_FIRST_FRAGMENT (1<<8) /* this is a first fragment of
234 * the frame */ 235 * the frame */
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
1181static 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
1181void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb, 1252void 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 */
36enum ieee80211_sta_info_flags { 39enum 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++;