Commit 0bc7d1d4 authored by Ping-Ke Shih's avatar Ping-Ke Shih Committed by Kalle Valo
Browse files

wifi: rtw89: pci: validate RX tag for RXQ and RPQ



PCI RX ring is a kind of read/write index ring, and DMA and ring index are
asynchronous, so suddenly driver gets newer index ahead before DMA. To
resolve this rare situation, we use a RX tag as helpers to make sure DMA
is done.

The RX tag is a 13-bit value, and range is from 1 ~ 0x1FFF, but 0 isn't
used so should be skipped.

Only enable this validation to coming WiFi 7 chips, because existing
chips use different design and don't really meet this situation.

Add missed rx_ring_eq_is_full for 8851BE by the way.

Signed-off-by: default avatarPing-Ke Shih <pkshih@realtek.com>
Signed-off-by: default avatarKalle Valo <kvalo@kernel.org>
Link: https://msgid.link/20240121071826.10159-4-pkshih@realtek.com
parent 26cdaee4
Loading
Loading
Loading
Loading
+54 −6
Original line number Diff line number Diff line
@@ -155,7 +155,7 @@ static void rtw89_pci_sync_skb_for_device(struct rtw89_dev *rtwdev,
				   DMA_FROM_DEVICE);
}

static int rtw89_pci_rxbd_info_update(struct rtw89_dev *rtwdev,
static void rtw89_pci_rxbd_info_update(struct rtw89_dev *rtwdev,
				       struct sk_buff *skb)
{
	struct rtw89_pci_rxbd_info *rxbd_info;
@@ -166,8 +166,56 @@ static int rtw89_pci_rxbd_info_update(struct rtw89_dev *rtwdev,
	rx_info->ls = le32_get_bits(rxbd_info->dword, RTW89_PCI_RXBD_LS);
	rx_info->len = le32_get_bits(rxbd_info->dword, RTW89_PCI_RXBD_WRITE_SIZE);
	rx_info->tag = le32_get_bits(rxbd_info->dword, RTW89_PCI_RXBD_TAG);
}

static int rtw89_pci_validate_rx_tag(struct rtw89_dev *rtwdev,
				     struct rtw89_pci_rx_ring *rx_ring,
				     struct sk_buff *skb)
{
	struct rtw89_pci_rx_info *rx_info = RTW89_PCI_RX_SKB_CB(skb);
	const struct rtw89_pci_info *info = rtwdev->pci_info;
	u32 target_rx_tag;

	if (!info->check_rx_tag)
		return 0;

	/* valid range is 1 ~ 0x1FFF */
	if (rx_ring->target_rx_tag == 0)
		target_rx_tag = 1;
	else
		target_rx_tag = rx_ring->target_rx_tag;

	if (rx_info->tag != target_rx_tag) {
		rtw89_debug(rtwdev, RTW89_DBG_UNEXP, "mismatch RX tag 0x%x 0x%x\n",
			    rx_info->tag, target_rx_tag);
		return -EAGAIN;
	}

	return 0;
}

static
int rtw89_pci_sync_skb_for_device_and_validate_rx_info(struct rtw89_dev *rtwdev,
						       struct rtw89_pci_rx_ring *rx_ring,
						       struct sk_buff *skb)
{
	struct rtw89_pci_rx_info *rx_info = RTW89_PCI_RX_SKB_CB(skb);
	int rx_tag_retry = 100;
	int ret;

	do {
		rtw89_pci_sync_skb_for_cpu(rtwdev, skb);
		rtw89_pci_rxbd_info_update(rtwdev, skb);

		ret = rtw89_pci_validate_rx_tag(rtwdev, rx_ring, skb);
		if (ret != -EAGAIN)
			break;
	} while (rx_tag_retry--);

	/* update target rx_tag for next RX */
	rx_ring->target_rx_tag = rx_info->tag + 1;

	return ret;
}

static void rtw89_pci_ctrl_txdma_ch_pcie(struct rtw89_dev *rtwdev, bool enable)
@@ -259,9 +307,8 @@ static u32 rtw89_pci_rxbd_deliver_skbs(struct rtw89_dev *rtwdev,

	skb_idx = rtw89_pci_get_rx_skb_idx(rtwdev, bd_ring);
	skb = rx_ring->buf[skb_idx];
	rtw89_pci_sync_skb_for_cpu(rtwdev, skb);

	ret = rtw89_pci_rxbd_info_update(rtwdev, skb);
	ret = rtw89_pci_sync_skb_for_device_and_validate_rx_info(rtwdev, rx_ring, skb);
	if (ret) {
		rtw89_err(rtwdev, "failed to update %d RXBD info: %d\n",
			  bd_ring->wp, ret);
@@ -549,9 +596,8 @@ static u32 rtw89_pci_release_tx_skbs(struct rtw89_dev *rtwdev,

	skb_idx = rtw89_pci_get_rx_skb_idx(rtwdev, bd_ring);
	skb = rx_ring->buf[skb_idx];
	rtw89_pci_sync_skb_for_cpu(rtwdev, skb);

	ret = rtw89_pci_rxbd_info_update(rtwdev, skb);
	ret = rtw89_pci_sync_skb_for_device_and_validate_rx_info(rtwdev, rx_ring, skb);
	if (ret) {
		rtw89_err(rtwdev, "failed to update %d RXBD info: %d\n",
			  bd_ring->wp, ret);
@@ -1550,6 +1596,7 @@ static void rtw89_pci_reset_trx_rings(struct rtw89_dev *rtwdev)
		bd_ring->rp = 0;
		rx_ring->diliver_skb = NULL;
		rx_ring->diliver_desc.ready = false;
		rx_ring->target_rx_tag = 0;

		rtw89_write16(rtwdev, addr_num, bd_ring->len);
		rtw89_write32(rtwdev, addr_desa_l, bd_ring->dma);
@@ -3213,6 +3260,7 @@ static int rtw89_pci_alloc_rx_ring(struct rtw89_dev *rtwdev,
	rx_ring->buf_sz = buf_sz;
	rx_ring->diliver_skb = NULL;
	rx_ring->diliver_desc.ready = false;
	rx_ring->target_rx_tag = 0;

	for (i = 0; i < len; i++) {
		skb = dev_alloc_skb(buf_sz);
+3 −1
Original line number Diff line number Diff line
@@ -1235,6 +1235,7 @@ struct rtw89_pci_info {
	enum mac_ax_pcie_func_ctrl io_rcy_en;
	enum mac_ax_io_rcy_tmr io_rcy_tmr;
	bool rx_ring_eq_is_full;
	bool check_rx_tag;

	u32 init_cfg_reg;
	u32 txhci_en_bit;
@@ -1277,7 +1278,7 @@ struct rtw89_pci_tx_data {

struct rtw89_pci_rx_info {
	dma_addr_t dma;
	u32 fs:1, ls:1, tag:11, len:14;
	u32 fs:1, ls:1, tag:13, len:14;
};

#define RTW89_PCI_TXBD_OPTION_LS	BIT(14)
@@ -1406,6 +1407,7 @@ struct rtw89_pci_rx_ring {
	u32 buf_sz;
	struct sk_buff *diliver_skb;
	struct rtw89_rx_desc_info diliver_desc;
	u32 target_rx_tag:13;
};

struct rtw89_pci_isrs {
+2 −0
Original line number Diff line number Diff line
@@ -25,6 +25,8 @@ static const struct rtw89_pci_info rtw8851b_pci_info = {
	.autok_en		= MAC_AX_PCIE_DISABLE,
	.io_rcy_en		= MAC_AX_PCIE_DISABLE,
	.io_rcy_tmr		= MAC_AX_IO_RCY_ANA_TMR_6MS,
	.rx_ring_eq_is_full	= false,
	.check_rx_tag		= false,

	.init_cfg_reg		= R_AX_PCIE_INIT_CFG1,
	.txhci_en_bit		= B_AX_TXHCI_EN,
+1 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ static const struct rtw89_pci_info rtw8852a_pci_info = {
	.io_rcy_en		= MAC_AX_PCIE_DISABLE,
	.io_rcy_tmr		= MAC_AX_IO_RCY_ANA_TMR_6MS,
	.rx_ring_eq_is_full	= false,
	.check_rx_tag		= false,

	.init_cfg_reg		= R_AX_PCIE_INIT_CFG1,
	.txhci_en_bit		= B_AX_TXHCI_EN,
+1 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ static const struct rtw89_pci_info rtw8852b_pci_info = {
	.io_rcy_en		= MAC_AX_PCIE_DISABLE,
	.io_rcy_tmr		= MAC_AX_IO_RCY_ANA_TMR_6MS,
	.rx_ring_eq_is_full	= false,
	.check_rx_tag		= false,

	.init_cfg_reg		= R_AX_PCIE_INIT_CFG1,
	.txhci_en_bit		= B_AX_TXHCI_EN,
Loading