diff options
author | Arik Nemtsov <arik@wizery.com> | 2012-05-18 00:46:38 -0400 |
---|---|---|
committer | Luciano Coelho <coelho@ti.com> | 2012-06-06 12:28:05 -0400 |
commit | 6639611467f34038aa63c5cb9f8d9e48171d6022 (patch) | |
tree | 22a03e2ce7cf2eaf53d2dba1705c496297abf30e /drivers | |
parent | 32bb2c03f990d015c0fec67e9134ea8625aaf784 (diff) |
wlcore: add stop reason bitmap for waking/starting queues
Allow the driver to wake/stop the queues for multiple reasons. A queue
is started when no stop-reasons exist.
Convert all wake/stop queue calls to use the new API.
Before, a stopped queue was almost synonymous a high-watermark on Tx.
Remove a bit of code in wl12xx_tx_reset() that relied on it.
Internal packets arriving from mac80211 are also discarded when a queue
is stopped. A notable exception to this is the watermark reason, which
is a "soft"-stop reason. We allow traffic to gradually come to a halt,
but we don't mind spurious packets here and there. This is merely a flow
regulation mechanism.
Based on a similar patch by Eliad Peller <eliadWizery.com>.
Signed-off-by: Arik Nemtsov <arik@wizery.com>
Signed-off-by: Luciano Coelho <coelho@ti.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wireless/ti/wlcore/main.c | 21 | ||||
-rw-r--r-- | drivers/net/wireless/ti/wlcore/tx.c | 109 | ||||
-rw-r--r-- | drivers/net/wireless/ti/wlcore/tx.h | 21 | ||||
-rw-r--r-- | drivers/net/wireless/ti/wlcore/wlcore.h | 2 |
4 files changed, 132 insertions, 21 deletions
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 0f25d4eea037..bbab19a1ce8a 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c | |||
@@ -865,7 +865,7 @@ static void wl1271_recovery_work(struct work_struct *work) | |||
865 | } | 865 | } |
866 | 866 | ||
867 | /* Prevent spurious TX during FW restart */ | 867 | /* Prevent spurious TX during FW restart */ |
868 | ieee80211_stop_queues(wl->hw); | 868 | wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_FW_RESTART); |
869 | 869 | ||
870 | if (wl->sched_scanning) { | 870 | if (wl->sched_scanning) { |
871 | ieee80211_sched_scan_stopped(wl->hw); | 871 | ieee80211_sched_scan_stopped(wl->hw); |
@@ -890,7 +890,7 @@ static void wl1271_recovery_work(struct work_struct *work) | |||
890 | * Its safe to enable TX now - the queues are stopped after a request | 890 | * Its safe to enable TX now - the queues are stopped after a request |
891 | * to restart the HW. | 891 | * to restart the HW. |
892 | */ | 892 | */ |
893 | ieee80211_wake_queues(wl->hw); | 893 | wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_FW_RESTART); |
894 | return; | 894 | return; |
895 | out_unlock: | 895 | out_unlock: |
896 | mutex_unlock(&wl->mutex); | 896 | mutex_unlock(&wl->mutex); |
@@ -1107,9 +1107,16 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) | |||
1107 | 1107 | ||
1108 | spin_lock_irqsave(&wl->wl_lock, flags); | 1108 | spin_lock_irqsave(&wl->wl_lock, flags); |
1109 | 1109 | ||
1110 | /* queue the packet */ | 1110 | /* |
1111 | * drop the packet if the link is invalid or the queue is stopped | ||
1112 | * for any reason but watermark. Watermark is a "soft"-stop so we | ||
1113 | * allow these packets through. | ||
1114 | */ | ||
1111 | if (hlid == WL12XX_INVALID_LINK_ID || | 1115 | if (hlid == WL12XX_INVALID_LINK_ID || |
1112 | (wlvif && !test_bit(hlid, wlvif->links_map))) { | 1116 | (wlvif && !test_bit(hlid, wlvif->links_map)) || |
1117 | (wlcore_is_queue_stopped(wl, q) && | ||
1118 | !wlcore_is_queue_stopped_by_reason(wl, q, | ||
1119 | WLCORE_QUEUE_STOP_REASON_WATERMARK))) { | ||
1113 | wl1271_debug(DEBUG_TX, "DROP skb hlid %d q %d", hlid, q); | 1120 | wl1271_debug(DEBUG_TX, "DROP skb hlid %d q %d", hlid, q); |
1114 | ieee80211_free_txskb(hw, skb); | 1121 | ieee80211_free_txskb(hw, skb); |
1115 | goto out; | 1122 | goto out; |
@@ -1127,8 +1134,8 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) | |||
1127 | */ | 1134 | */ |
1128 | if (wl->tx_queue_count[q] >= WL1271_TX_QUEUE_HIGH_WATERMARK) { | 1135 | if (wl->tx_queue_count[q] >= WL1271_TX_QUEUE_HIGH_WATERMARK) { |
1129 | wl1271_debug(DEBUG_TX, "op_tx: stopping queues for q %d", q); | 1136 | wl1271_debug(DEBUG_TX, "op_tx: stopping queues for q %d", q); |
1130 | ieee80211_stop_queue(wl->hw, mapping); | 1137 | wlcore_stop_queue_locked(wl, q, |
1131 | set_bit(q, &wl->stopped_queues_map); | 1138 | WLCORE_QUEUE_STOP_REASON_WATERMARK); |
1132 | } | 1139 | } |
1133 | 1140 | ||
1134 | /* | 1141 | /* |
@@ -1711,7 +1718,7 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) | |||
1711 | cancel_delayed_work_sync(&wl->connection_loss_work); | 1718 | cancel_delayed_work_sync(&wl->connection_loss_work); |
1712 | 1719 | ||
1713 | /* let's notify MAC80211 about the remaining pending TX frames */ | 1720 | /* let's notify MAC80211 about the remaining pending TX frames */ |
1714 | wl12xx_tx_reset(wl, true); | 1721 | wl12xx_tx_reset(wl); |
1715 | mutex_lock(&wl->mutex); | 1722 | mutex_lock(&wl->mutex); |
1716 | 1723 | ||
1717 | wl1271_power_off(wl); | 1724 | wl1271_power_off(wl); |
diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index 0949ab1f5972..f68567b1524c 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c | |||
@@ -443,18 +443,15 @@ u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set, | |||
443 | 443 | ||
444 | void wl1271_handle_tx_low_watermark(struct wl1271 *wl) | 444 | void wl1271_handle_tx_low_watermark(struct wl1271 *wl) |
445 | { | 445 | { |
446 | unsigned long flags; | ||
447 | int i; | 446 | int i; |
448 | 447 | ||
449 | for (i = 0; i < NUM_TX_QUEUES; i++) { | 448 | for (i = 0; i < NUM_TX_QUEUES; i++) { |
450 | if (test_bit(i, &wl->stopped_queues_map) && | 449 | if (wlcore_is_queue_stopped_by_reason(wl, i, |
450 | WLCORE_QUEUE_STOP_REASON_WATERMARK) && | ||
451 | wl->tx_queue_count[i] <= WL1271_TX_QUEUE_LOW_WATERMARK) { | 451 | wl->tx_queue_count[i] <= WL1271_TX_QUEUE_LOW_WATERMARK) { |
452 | /* firmware buffer has space, restart queues */ | 452 | /* firmware buffer has space, restart queues */ |
453 | spin_lock_irqsave(&wl->wl_lock, flags); | 453 | wlcore_wake_queue(wl, i, |
454 | ieee80211_wake_queue(wl->hw, | 454 | WLCORE_QUEUE_STOP_REASON_WATERMARK); |
455 | wl1271_tx_get_mac80211_queue(i)); | ||
456 | clear_bit(i, &wl->stopped_queues_map); | ||
457 | spin_unlock_irqrestore(&wl->wl_lock, flags); | ||
458 | } | 455 | } |
459 | } | 456 | } |
460 | } | 457 | } |
@@ -963,7 +960,7 @@ void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif) | |||
963 | 960 | ||
964 | } | 961 | } |
965 | /* caller must hold wl->mutex and TX must be stopped */ | 962 | /* caller must hold wl->mutex and TX must be stopped */ |
966 | void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues) | 963 | void wl12xx_tx_reset(struct wl1271 *wl) |
967 | { | 964 | { |
968 | int i; | 965 | int i; |
969 | struct sk_buff *skb; | 966 | struct sk_buff *skb; |
@@ -978,15 +975,12 @@ void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues) | |||
978 | wl->tx_queue_count[i] = 0; | 975 | wl->tx_queue_count[i] = 0; |
979 | } | 976 | } |
980 | 977 | ||
981 | wl->stopped_queues_map = 0; | ||
982 | |||
983 | /* | 978 | /* |
984 | * Make sure the driver is at a consistent state, in case this | 979 | * Make sure the driver is at a consistent state, in case this |
985 | * function is called from a context other than interface removal. | 980 | * function is called from a context other than interface removal. |
986 | * This call will always wake the TX queues. | 981 | * This call will always wake the TX queues. |
987 | */ | 982 | */ |
988 | if (reset_tx_queues) | 983 | wl1271_handle_tx_low_watermark(wl); |
989 | wl1271_handle_tx_low_watermark(wl); | ||
990 | 984 | ||
991 | for (i = 0; i < wl->num_tx_desc; i++) { | 985 | for (i = 0; i < wl->num_tx_desc; i++) { |
992 | if (wl->tx_frames[i] == NULL) | 986 | if (wl->tx_frames[i] == NULL) |
@@ -1060,3 +1054,94 @@ u32 wl1271_tx_min_rate_get(struct wl1271 *wl, u32 rate_set) | |||
1060 | 1054 | ||
1061 | return BIT(__ffs(rate_set)); | 1055 | return BIT(__ffs(rate_set)); |
1062 | } | 1056 | } |
1057 | |||
1058 | void wlcore_stop_queue_locked(struct wl1271 *wl, u8 queue, | ||
1059 | enum wlcore_queue_stop_reason reason) | ||
1060 | { | ||
1061 | bool stopped = !!wl->queue_stop_reasons[queue]; | ||
1062 | |||
1063 | /* queue should not be stopped for this reason */ | ||
1064 | WARN_ON(test_and_set_bit(reason, &wl->queue_stop_reasons[queue])); | ||
1065 | |||
1066 | if (stopped) | ||
1067 | return; | ||
1068 | |||
1069 | ieee80211_stop_queue(wl->hw, wl1271_tx_get_mac80211_queue(queue)); | ||
1070 | } | ||
1071 | |||
1072 | void wlcore_stop_queue(struct wl1271 *wl, u8 queue, | ||
1073 | enum wlcore_queue_stop_reason reason) | ||
1074 | { | ||
1075 | unsigned long flags; | ||
1076 | |||
1077 | spin_lock_irqsave(&wl->wl_lock, flags); | ||
1078 | wlcore_stop_queue_locked(wl, queue, reason); | ||
1079 | spin_unlock_irqrestore(&wl->wl_lock, flags); | ||
1080 | } | ||
1081 | |||
1082 | void wlcore_wake_queue(struct wl1271 *wl, u8 queue, | ||
1083 | enum wlcore_queue_stop_reason reason) | ||
1084 | { | ||
1085 | unsigned long flags; | ||
1086 | |||
1087 | spin_lock_irqsave(&wl->wl_lock, flags); | ||
1088 | |||
1089 | /* queue should not be clear for this reason */ | ||
1090 | WARN_ON(!test_and_clear_bit(reason, &wl->queue_stop_reasons[queue])); | ||
1091 | |||
1092 | if (wl->queue_stop_reasons[queue]) | ||
1093 | goto out; | ||
1094 | |||
1095 | ieee80211_wake_queue(wl->hw, wl1271_tx_get_mac80211_queue(queue)); | ||
1096 | |||
1097 | out: | ||
1098 | spin_unlock_irqrestore(&wl->wl_lock, flags); | ||
1099 | } | ||
1100 | |||
1101 | void wlcore_stop_queues(struct wl1271 *wl, | ||
1102 | enum wlcore_queue_stop_reason reason) | ||
1103 | { | ||
1104 | int i; | ||
1105 | |||
1106 | for (i = 0; i < NUM_TX_QUEUES; i++) | ||
1107 | wlcore_stop_queue(wl, i, reason); | ||
1108 | } | ||
1109 | |||
1110 | void wlcore_wake_queues(struct wl1271 *wl, | ||
1111 | enum wlcore_queue_stop_reason reason) | ||
1112 | { | ||
1113 | int i; | ||
1114 | |||
1115 | for (i = 0; i < NUM_TX_QUEUES; i++) | ||
1116 | wlcore_wake_queue(wl, i, reason); | ||
1117 | } | ||
1118 | |||
1119 | void wlcore_reset_stopped_queues(struct wl1271 *wl) | ||
1120 | { | ||
1121 | int i; | ||
1122 | unsigned long flags; | ||
1123 | |||
1124 | spin_lock_irqsave(&wl->wl_lock, flags); | ||
1125 | |||
1126 | for (i = 0; i < NUM_TX_QUEUES; i++) { | ||
1127 | if (!wl->queue_stop_reasons[i]) | ||
1128 | continue; | ||
1129 | |||
1130 | wl->queue_stop_reasons[i] = 0; | ||
1131 | ieee80211_wake_queue(wl->hw, | ||
1132 | wl1271_tx_get_mac80211_queue(i)); | ||
1133 | } | ||
1134 | |||
1135 | spin_unlock_irqrestore(&wl->wl_lock, flags); | ||
1136 | } | ||
1137 | |||
1138 | bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl, u8 queue, | ||
1139 | enum wlcore_queue_stop_reason reason) | ||
1140 | { | ||
1141 | return test_bit(reason, &wl->queue_stop_reasons[queue]); | ||
1142 | } | ||
1143 | |||
1144 | bool wlcore_is_queue_stopped(struct wl1271 *wl, u8 queue) | ||
1145 | { | ||
1146 | return !!wl->queue_stop_reasons[queue]; | ||
1147 | } | ||
diff --git a/drivers/net/wireless/ti/wlcore/tx.h b/drivers/net/wireless/ti/wlcore/tx.h index e24c436bf65f..6bf695681762 100644 --- a/drivers/net/wireless/ti/wlcore/tx.h +++ b/drivers/net/wireless/ti/wlcore/tx.h | |||
@@ -184,6 +184,11 @@ struct wl1271_tx_hw_res_if { | |||
184 | struct wl1271_tx_hw_res_descr tx_results_queue[TX_HW_RESULT_QUEUE_LEN]; | 184 | struct wl1271_tx_hw_res_descr tx_results_queue[TX_HW_RESULT_QUEUE_LEN]; |
185 | } __packed; | 185 | } __packed; |
186 | 186 | ||
187 | enum wlcore_queue_stop_reason { | ||
188 | WLCORE_QUEUE_STOP_REASON_WATERMARK, | ||
189 | WLCORE_QUEUE_STOP_REASON_FW_RESTART, | ||
190 | }; | ||
191 | |||
187 | static inline int wl1271_tx_get_queue(int queue) | 192 | static inline int wl1271_tx_get_queue(int queue) |
188 | { | 193 | { |
189 | switch (queue) { | 194 | switch (queue) { |
@@ -230,7 +235,7 @@ void wl1271_tx_work(struct work_struct *work); | |||
230 | void wl1271_tx_work_locked(struct wl1271 *wl); | 235 | void wl1271_tx_work_locked(struct wl1271 *wl); |
231 | void wl1271_tx_complete(struct wl1271 *wl); | 236 | void wl1271_tx_complete(struct wl1271 *wl); |
232 | void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif); | 237 | void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif); |
233 | void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues); | 238 | void wl12xx_tx_reset(struct wl1271 *wl); |
234 | void wl1271_tx_flush(struct wl1271 *wl); | 239 | void wl1271_tx_flush(struct wl1271 *wl); |
235 | u8 wlcore_rate_to_idx(struct wl1271 *wl, u8 rate, enum ieee80211_band band); | 240 | u8 wlcore_rate_to_idx(struct wl1271 *wl, u8 rate, enum ieee80211_band band); |
236 | u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set, | 241 | u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set, |
@@ -247,6 +252,20 @@ void wl12xx_rearm_rx_streaming(struct wl1271 *wl, unsigned long *active_hlids); | |||
247 | unsigned int wlcore_calc_packet_alignment(struct wl1271 *wl, | 252 | unsigned int wlcore_calc_packet_alignment(struct wl1271 *wl, |
248 | unsigned int packet_length); | 253 | unsigned int packet_length); |
249 | void wl1271_free_tx_id(struct wl1271 *wl, int id); | 254 | void wl1271_free_tx_id(struct wl1271 *wl, int id); |
255 | void wlcore_stop_queue_locked(struct wl1271 *wl, u8 queue, | ||
256 | enum wlcore_queue_stop_reason reason); | ||
257 | void wlcore_stop_queue(struct wl1271 *wl, u8 queue, | ||
258 | enum wlcore_queue_stop_reason reason); | ||
259 | void wlcore_wake_queue(struct wl1271 *wl, u8 queue, | ||
260 | enum wlcore_queue_stop_reason reason); | ||
261 | void wlcore_stop_queues(struct wl1271 *wl, | ||
262 | enum wlcore_queue_stop_reason reason); | ||
263 | void wlcore_wake_queues(struct wl1271 *wl, | ||
264 | enum wlcore_queue_stop_reason reason); | ||
265 | void wlcore_reset_stopped_queues(struct wl1271 *wl); | ||
266 | bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl, u8 queue, | ||
267 | enum wlcore_queue_stop_reason reason); | ||
268 | bool wlcore_is_queue_stopped(struct wl1271 *wl, u8 queue); | ||
250 | 269 | ||
251 | /* from main.c */ | 270 | /* from main.c */ |
252 | void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid); | 271 | void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid); |
diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 5274ace6c8e4..681054331fd2 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h | |||
@@ -209,7 +209,7 @@ struct wl1271 { | |||
209 | 209 | ||
210 | /* Frames scheduled for transmission, not handled yet */ | 210 | /* Frames scheduled for transmission, not handled yet */ |
211 | int tx_queue_count[NUM_TX_QUEUES]; | 211 | int tx_queue_count[NUM_TX_QUEUES]; |
212 | long stopped_queues_map; | 212 | unsigned long queue_stop_reasons[NUM_TX_QUEUES]; |
213 | 213 | ||
214 | /* Frames received, not handled yet by mac80211 */ | 214 | /* Frames received, not handled yet by mac80211 */ |
215 | struct sk_buff_head deferred_rx_queue; | 215 | struct sk_buff_head deferred_rx_queue; |