diff options
author | Sujith <Sujith.Manoharan@atheros.com> | 2010-05-06 05:15:47 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-05-07 14:55:52 -0400 |
commit | 9c6dda4e2dfea970a7105e3805f0195bc3079f2f (patch) | |
tree | 3b255fefdca205e5f38032236faeb1d7e433a2a8 /drivers/net/wireless | |
parent | 0aaffa9b9699894aab3266195a529baf9f96ac29 (diff) |
ath9k_htc: Fix beaconing in IBSS mode
The current way of managing beaconing in ad-hoc
mode has a subtle race - the beacon obtained from mac80211
is freed in the SWBA handler rather than the TX
completion routine. But transmission of beacons goes
through the normal SKB queue maintained in hif_usb,
leading to a situation where __skb_dequeue() in the TX
completion handler goes kaput.
Fix this by simply getting a beacon from mac80211 for
every SWBA and free it in its completion routine.
Signed-off-by: Sujith <Sujith.Manoharan@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/htc.h | 5 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/htc_drv_beacon.c | 39 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/htc_drv_init.c | 2 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/htc_drv_main.c | 12 |
4 files changed, 13 insertions, 45 deletions
diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h index 1ae18bbc4d9e..ad556aa8da39 100644 --- a/drivers/net/wireless/ath/ath9k/htc.h +++ b/drivers/net/wireless/ath/ath9k/htc.h | |||
@@ -356,7 +356,6 @@ struct ath9k_htc_priv { | |||
356 | u16 seq_no; | 356 | u16 seq_no; |
357 | u32 bmiss_cnt; | 357 | u32 bmiss_cnt; |
358 | 358 | ||
359 | struct sk_buff *beacon; | ||
360 | spinlock_t beacon_lock; | 359 | spinlock_t beacon_lock; |
361 | 360 | ||
362 | bool tx_queues_stop; | 361 | bool tx_queues_stop; |
@@ -408,13 +407,13 @@ static inline void ath_read_cachesize(struct ath_common *common, int *csz) | |||
408 | void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv, | 407 | void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv, |
409 | struct ieee80211_vif *vif); | 408 | struct ieee80211_vif *vif); |
410 | void ath9k_htc_swba(struct ath9k_htc_priv *priv, u8 beacon_pending); | 409 | void ath9k_htc_swba(struct ath9k_htc_priv *priv, u8 beacon_pending); |
411 | void ath9k_htc_beacon_update(struct ath9k_htc_priv *priv, | ||
412 | struct ieee80211_vif *vif); | ||
413 | 410 | ||
414 | void ath9k_htc_rxep(void *priv, struct sk_buff *skb, | 411 | void ath9k_htc_rxep(void *priv, struct sk_buff *skb, |
415 | enum htc_endpoint_id ep_id); | 412 | enum htc_endpoint_id ep_id); |
416 | void ath9k_htc_txep(void *priv, struct sk_buff *skb, enum htc_endpoint_id ep_id, | 413 | void ath9k_htc_txep(void *priv, struct sk_buff *skb, enum htc_endpoint_id ep_id, |
417 | bool txok); | 414 | bool txok); |
415 | void ath9k_htc_beaconep(void *drv_priv, struct sk_buff *skb, | ||
416 | enum htc_endpoint_id ep_id, bool txok); | ||
418 | 417 | ||
419 | void ath9k_htc_station_work(struct work_struct *work); | 418 | void ath9k_htc_station_work(struct work_struct *work); |
420 | void ath9k_htc_aggr_work(struct work_struct *work); | 419 | void ath9k_htc_aggr_work(struct work_struct *work); |
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c index 7cb55f5b071c..c10c7d002eb7 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c | |||
@@ -165,22 +165,10 @@ static void ath9k_htc_beacon_config_adhoc(struct ath9k_htc_priv *priv, | |||
165 | WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask); | 165 | WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask); |
166 | } | 166 | } |
167 | 167 | ||
168 | void ath9k_htc_beacon_update(struct ath9k_htc_priv *priv, | 168 | void ath9k_htc_beaconep(void *drv_priv, struct sk_buff *skb, |
169 | struct ieee80211_vif *vif) | 169 | enum htc_endpoint_id ep_id, bool txok) |
170 | { | 170 | { |
171 | struct ath_common *common = ath9k_hw_common(priv->ah); | 171 | dev_kfree_skb_any(skb); |
172 | |||
173 | spin_lock_bh(&priv->beacon_lock); | ||
174 | |||
175 | if (priv->beacon) | ||
176 | dev_kfree_skb_any(priv->beacon); | ||
177 | |||
178 | priv->beacon = ieee80211_beacon_get(priv->hw, vif); | ||
179 | if (!priv->beacon) | ||
180 | ath_print(common, ATH_DBG_BEACON, | ||
181 | "Unable to allocate beacon\n"); | ||
182 | |||
183 | spin_unlock_bh(&priv->beacon_lock); | ||
184 | } | 172 | } |
185 | 173 | ||
186 | void ath9k_htc_swba(struct ath9k_htc_priv *priv, u8 beacon_pending) | 174 | void ath9k_htc_swba(struct ath9k_htc_priv *priv, u8 beacon_pending) |
@@ -189,6 +177,7 @@ void ath9k_htc_swba(struct ath9k_htc_priv *priv, u8 beacon_pending) | |||
189 | struct tx_beacon_header beacon_hdr; | 177 | struct tx_beacon_header beacon_hdr; |
190 | struct ath9k_htc_tx_ctl tx_ctl; | 178 | struct ath9k_htc_tx_ctl tx_ctl; |
191 | struct ieee80211_tx_info *info; | 179 | struct ieee80211_tx_info *info; |
180 | struct sk_buff *beacon; | ||
192 | u8 *tx_fhdr; | 181 | u8 *tx_fhdr; |
193 | 182 | ||
194 | memset(&beacon_hdr, 0, sizeof(struct tx_beacon_header)); | 183 | memset(&beacon_hdr, 0, sizeof(struct tx_beacon_header)); |
@@ -207,25 +196,17 @@ void ath9k_htc_swba(struct ath9k_htc_priv *priv, u8 beacon_pending) | |||
207 | return; | 196 | return; |
208 | } | 197 | } |
209 | 198 | ||
210 | if (unlikely(priv->beacon == NULL)) { | ||
211 | spin_unlock_bh(&priv->beacon_lock); | ||
212 | return; | ||
213 | } | ||
214 | |||
215 | /* Free the old SKB first */ | ||
216 | dev_kfree_skb_any(priv->beacon); | ||
217 | |||
218 | /* Get a new beacon */ | 199 | /* Get a new beacon */ |
219 | priv->beacon = ieee80211_beacon_get(priv->hw, priv->vif); | 200 | beacon = ieee80211_beacon_get(priv->hw, priv->vif); |
220 | if (!priv->beacon) { | 201 | if (!beacon) { |
221 | spin_unlock_bh(&priv->beacon_lock); | 202 | spin_unlock_bh(&priv->beacon_lock); |
222 | return; | 203 | return; |
223 | } | 204 | } |
224 | 205 | ||
225 | info = IEEE80211_SKB_CB(priv->beacon); | 206 | info = IEEE80211_SKB_CB(beacon); |
226 | if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { | 207 | if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { |
227 | struct ieee80211_hdr *hdr = | 208 | struct ieee80211_hdr *hdr = |
228 | (struct ieee80211_hdr *) priv->beacon->data; | 209 | (struct ieee80211_hdr *) beacon->data; |
229 | priv->seq_no += 0x10; | 210 | priv->seq_no += 0x10; |
230 | hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); | 211 | hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); |
231 | hdr->seq_ctrl |= cpu_to_le16(priv->seq_no); | 212 | hdr->seq_ctrl |= cpu_to_le16(priv->seq_no); |
@@ -233,10 +214,10 @@ void ath9k_htc_swba(struct ath9k_htc_priv *priv, u8 beacon_pending) | |||
233 | 214 | ||
234 | tx_ctl.type = ATH9K_HTC_NORMAL; | 215 | tx_ctl.type = ATH9K_HTC_NORMAL; |
235 | beacon_hdr.vif_index = avp->index; | 216 | beacon_hdr.vif_index = avp->index; |
236 | tx_fhdr = skb_push(priv->beacon, sizeof(beacon_hdr)); | 217 | tx_fhdr = skb_push(beacon, sizeof(beacon_hdr)); |
237 | memcpy(tx_fhdr, (u8 *) &beacon_hdr, sizeof(beacon_hdr)); | 218 | memcpy(tx_fhdr, (u8 *) &beacon_hdr, sizeof(beacon_hdr)); |
238 | 219 | ||
239 | htc_send(priv->htc, priv->beacon, priv->beacon_ep, &tx_ctl); | 220 | htc_send(priv->htc, beacon, priv->beacon_ep, &tx_ctl); |
240 | 221 | ||
241 | spin_unlock_bh(&priv->beacon_lock); | 222 | spin_unlock_bh(&priv->beacon_lock); |
242 | } | 223 | } |
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index 701f2ef5a440..17111fc1d2cc 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c | |||
@@ -144,7 +144,7 @@ static int ath9k_init_htc_services(struct ath9k_htc_priv *priv) | |||
144 | goto err; | 144 | goto err; |
145 | 145 | ||
146 | /* Beacon */ | 146 | /* Beacon */ |
147 | ret = ath9k_htc_connect_svc(priv, WMI_BEACON_SVC, NULL, | 147 | ret = ath9k_htc_connect_svc(priv, WMI_BEACON_SVC, ath9k_htc_beaconep, |
148 | &priv->beacon_ep); | 148 | &priv->beacon_ep); |
149 | if (ret) | 149 | if (ret) |
150 | goto err; | 150 | goto err; |
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index ca7f3a78eb11..7c9e33ba95ab 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c | |||
@@ -1313,15 +1313,6 @@ static void ath9k_htc_remove_interface(struct ieee80211_hw *hw, | |||
1313 | priv->nvifs--; | 1313 | priv->nvifs--; |
1314 | 1314 | ||
1315 | ath9k_htc_remove_station(priv, vif, NULL); | 1315 | ath9k_htc_remove_station(priv, vif, NULL); |
1316 | |||
1317 | if (vif->type == NL80211_IFTYPE_ADHOC) { | ||
1318 | spin_lock_bh(&priv->beacon_lock); | ||
1319 | if (priv->beacon) | ||
1320 | dev_kfree_skb_any(priv->beacon); | ||
1321 | priv->beacon = NULL; | ||
1322 | spin_unlock_bh(&priv->beacon_lock); | ||
1323 | } | ||
1324 | |||
1325 | priv->vif = NULL; | 1316 | priv->vif = NULL; |
1326 | 1317 | ||
1327 | mutex_unlock(&priv->mutex); | 1318 | mutex_unlock(&priv->mutex); |
@@ -1590,9 +1581,6 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw, | |||
1590 | ath9k_htc_beacon_config(priv, vif); | 1581 | ath9k_htc_beacon_config(priv, vif); |
1591 | } | 1582 | } |
1592 | 1583 | ||
1593 | if (changed & BSS_CHANGED_BEACON) | ||
1594 | ath9k_htc_beacon_update(priv, vif); | ||
1595 | |||
1596 | if ((changed & BSS_CHANGED_BEACON_ENABLED) && | 1584 | if ((changed & BSS_CHANGED_BEACON_ENABLED) && |
1597 | !bss_conf->enable_beacon) { | 1585 | !bss_conf->enable_beacon) { |
1598 | priv->op_flags &= ~OP_ENABLE_BEACON; | 1586 | priv->op_flags &= ~OP_ENABLE_BEACON; |