aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ath/ath10k/wmi.c
diff options
context:
space:
mode:
authorMichal Kazior <michal.kazior@tieto.com>2014-09-18 04:18:02 -0400
committerKalle Valo <kvalo@qca.qualcomm.com>2014-09-23 05:28:52 -0400
commit64badcb6d6459cc6f7b46f7d45e44c95ab874337 (patch)
treeb7b4cb38e44abc22ceadcc5fd4495f0f6fd5a025 /drivers/net/wireless/ath/ath10k/wmi.c
parentb25f32cb02155d68c690255ba846796a1c248fd3 (diff)
ath10k: workaround fw beaconing bug
Some firmware revisions don't wait for beacon tx completion before sending another SWBA event. This could lead to hardware using old (freed) beacon data in some cases, e.g. tx credit starvation combined with missed TBTT. This is very very rare. On non-IOMMU-enabled hosts this could be a possible security issue because hw could beacon some random data on the air. On IOMMU-enabled hosts DMAR faults would occur in most cases and target device would crash. Since there are no beacon tx completions (implicit nor explicit) propagated to host the only workaround for this is to allocate a DMA-coherent buffer for a lifetime of a vif and use it for all beacon tx commands. Worst case for this approach is some beacons may become corrupted, e.g. garbled IEs or out-of-date TIM bitmap. Keep the original beacon-related code as-is in case future firmware revisions solve this problem so that the old path can be easily re-enabled with a fw_feature flag. Signed-off-by: Michal Kazior <michal.kazior@tieto.com> Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
Diffstat (limited to 'drivers/net/wireless/ath/ath10k/wmi.c')
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi.c36
1 files changed, 22 insertions, 14 deletions
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index 2c42bd504b79..66392f6f1ab1 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -1579,6 +1579,7 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
1579 struct wmi_bcn_info *bcn_info; 1579 struct wmi_bcn_info *bcn_info;
1580 struct ath10k_vif *arvif; 1580 struct ath10k_vif *arvif;
1581 struct sk_buff *bcn; 1581 struct sk_buff *bcn;
1582 dma_addr_t paddr;
1582 int ret, vdev_id = 0; 1583 int ret, vdev_id = 0;
1583 1584
1584 ev = (struct wmi_host_swba_event *)skb->data; 1585 ev = (struct wmi_host_swba_event *)skb->data;
@@ -1647,22 +1648,29 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
1647 ath10k_warn(ar, "SWBA overrun on vdev %d\n", 1648 ath10k_warn(ar, "SWBA overrun on vdev %d\n",
1648 arvif->vdev_id); 1649 arvif->vdev_id);
1649 1650
1650 dma_unmap_single(arvif->ar->dev, 1651 ath10k_mac_vif_beacon_free(arvif);
1651 ATH10K_SKB_CB(arvif->beacon)->paddr,
1652 arvif->beacon->len, DMA_TO_DEVICE);
1653 dev_kfree_skb_any(arvif->beacon);
1654 arvif->beacon = NULL;
1655 } 1652 }
1656 1653
1657 ATH10K_SKB_CB(bcn)->paddr = dma_map_single(arvif->ar->dev, 1654 if (!arvif->beacon_buf) {
1658 bcn->data, bcn->len, 1655 paddr = dma_map_single(arvif->ar->dev, bcn->data,
1659 DMA_TO_DEVICE); 1656 bcn->len, DMA_TO_DEVICE);
1660 ret = dma_mapping_error(arvif->ar->dev, 1657 ret = dma_mapping_error(arvif->ar->dev, paddr);
1661 ATH10K_SKB_CB(bcn)->paddr); 1658 if (ret) {
1662 if (ret) { 1659 ath10k_warn(ar, "failed to map beacon: %d\n",
1663 ath10k_warn(ar, "failed to map beacon: %d\n", ret); 1660 ret);
1664 dev_kfree_skb_any(bcn); 1661 dev_kfree_skb_any(bcn);
1665 goto skip; 1662 goto skip;
1663 }
1664
1665 ATH10K_SKB_CB(bcn)->paddr = paddr;
1666 } else {
1667 if (bcn->len > IEEE80211_MAX_FRAME_LEN) {
1668 ath10k_warn(ar, "trimming beacon %d -> %d bytes!\n",
1669 bcn->len, IEEE80211_MAX_FRAME_LEN);
1670 skb_trim(bcn, IEEE80211_MAX_FRAME_LEN);
1671 }
1672 memcpy(arvif->beacon_buf, bcn->data, bcn->len);
1673 ATH10K_SKB_CB(bcn)->paddr = arvif->beacon_paddr;
1666 } 1674 }
1667 1675
1668 arvif->beacon = bcn; 1676 arvif->beacon = bcn;