| From e1adcc080163c31dfad21051431fc1af7939fa5f Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Fri, 22 Oct 2021 17:15:28 -0700 |
| Subject: wcn36xx: add proper DMA memory barriers in rx path |
| |
| From: Benjamin Li <benl@squareup.com> |
| |
| [ Upstream commit 9bfe38e064af5decba2ffce66a2958ab8b10eaa4 ] |
| |
| This is essentially exactly following the dma_wmb()/dma_rmb() usage |
| instructions in Documentation/memory-barriers.txt. |
| |
| The theoretical races here are: |
| |
| 1. DXE (the DMA Transfer Engine in the Wi-Fi subsystem) seeing the |
| dxe->ctrl & WCN36xx_DXE_CTRL_VLD write before the dxe->dst_addr_l |
| write, thus performing DMA into the wrong address. |
| |
| 2. CPU reading dxe->dst_addr_l before DXE unsets dxe->ctrl & |
| WCN36xx_DXE_CTRL_VLD. This should generally be harmless since DXE |
| doesn't write dxe->dst_addr_l (no risk of freeing the wrong skb). |
| |
| Fixes: 8e84c2582169 ("wcn36xx: mac80211 driver for Qualcomm WCN3660/WCN3680 hardware") |
| Signed-off-by: Benjamin Li <benl@squareup.com> |
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> |
| Link: https://lore.kernel.org/r/20211023001528.3077822-1-benl@squareup.com |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/net/wireless/ath/wcn36xx/dxe.c | 12 +++++++++++- |
| 1 file changed, 11 insertions(+), 1 deletion(-) |
| |
| diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.c b/drivers/net/wireless/ath/wcn36xx/dxe.c |
| index bab30f7a443ce..4da25e84793b7 100644 |
| --- a/drivers/net/wireless/ath/wcn36xx/dxe.c |
| +++ b/drivers/net/wireless/ath/wcn36xx/dxe.c |
| @@ -563,6 +563,10 @@ static int wcn36xx_rx_handle_packets(struct wcn36xx *wcn, |
| dxe = ctl->desc; |
| |
| while (!(READ_ONCE(dxe->ctrl) & WCN36xx_DXE_CTRL_VLD)) { |
| + /* do not read until we own DMA descriptor */ |
| + dma_rmb(); |
| + |
| + /* read/modify DMA descriptor */ |
| skb = ctl->skb; |
| dma_addr = dxe->dst_addr_l; |
| ret = wcn36xx_dxe_fill_skb(wcn->dev, ctl, GFP_ATOMIC); |
| @@ -573,9 +577,15 @@ static int wcn36xx_rx_handle_packets(struct wcn36xx *wcn, |
| dma_unmap_single(wcn->dev, dma_addr, WCN36XX_PKT_SIZE, |
| DMA_FROM_DEVICE); |
| wcn36xx_rx_skb(wcn, skb); |
| - } /* else keep old skb not submitted and use it for rx DMA */ |
| + } |
| + /* else keep old skb not submitted and reuse it for rx DMA |
| + * (dropping the packet that it contained) |
| + */ |
| |
| + /* flush descriptor changes before re-marking as valid */ |
| + dma_wmb(); |
| dxe->ctrl = ctrl; |
| + |
| ctl = ctl->next; |
| dxe = ctl->desc; |
| } |
| -- |
| 2.33.0 |
| |