diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index fd6624d12efe..6059e30a0590 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -1131,6 +1131,9 @@ rtw89_core_tx_update_desc_info(struct rtw89_dev *rtwdev, if (addr_cam->valid && desc_info->mlo) upd_wlan_hdr = true; + if (rtw89_is_tx_rpt_skb(tx_req->skb)) + rtw89_tx_rpt_init(rtwdev, tx_req); + is_bmc = (is_broadcast_ether_addr(hdr->addr1) || is_multicast_ether_addr(hdr->addr1)); @@ -5865,6 +5868,7 @@ int rtw89_core_init(struct rtw89_dev *rtwdev) wiphy_work_init(&rtwdev->cancel_6ghz_probe_work, rtw89_cancel_6ghz_probe_work); INIT_WORK(&rtwdev->load_firmware_work, rtw89_load_firmware_work); + spin_lock_init(&rtwdev->tx_rpt.skb_lock); skb_queue_head_init(&rtwdev->c2h_queue); rtw89_core_ppdu_sts_init(rtwdev); rtw89_traffic_stats_init(rtwdev, &rtwdev->stats); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 2914cc4e97d5..cb71bd87b193 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3518,6 +3518,15 @@ struct rtw89_phy_rate_pattern { #define RTW89_TX_LIFE_TIME 0x2 #define RTW89_TX_MACID_DROP 0x3 +#define RTW89_MAX_TX_RPTS 16 +#define RTW89_MAX_TX_RPTS_MASK (RTW89_MAX_TX_RPTS - 1) +struct rtw89_tx_rpt { + struct sk_buff *skbs[RTW89_MAX_TX_RPTS]; + /* protect skbs array access/modification */ + spinlock_t skb_lock; + atomic_t sn; +}; + #define RTW89_TX_WAIT_WORK_TIMEOUT msecs_to_jiffies(500) struct rtw89_tx_wait_info { struct rcu_head rcu_head; @@ -3529,6 +3538,8 @@ struct rtw89_tx_wait_info { struct rtw89_tx_skb_data { struct rtw89_tx_wait_info __rcu *wait; + u8 tx_rpt_sn; + u8 tx_pkt_cnt_lmt; u8 hci_priv[]; }; @@ -3698,6 +3709,7 @@ struct rtw89_hci_info { u32 rpwm_addr; u32 cpwm_addr; bool paused; + bool tx_rpt_enabled; }; struct rtw89_chip_ops { @@ -6021,6 +6033,8 @@ struct rtw89_dev { struct list_head tx_waits; struct wiphy_delayed_work tx_wait_work; + struct rtw89_tx_rpt tx_rpt; + struct rtw89_cam_info cam_info; struct sk_buff_head c2h_queue; diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 47048d125c01..0a8474002cb7 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -5491,7 +5491,10 @@ rtw89_mac_c2h_mcc_status_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 static void rtw89_mac_c2h_tx_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) { + struct rtw89_tx_rpt *tx_rpt = &rtwdev->tx_rpt; + struct rtw89_tx_skb_data *skb_data; u8 sw_define, tx_status, txcnt; + struct sk_buff *skb; if (rtwdev->chip->chip_id == RTL8922A) { const struct rtw89_c2h_mac_tx_rpt_v2 *rpt_v2; @@ -5520,6 +5523,35 @@ rtw89_mac_c2h_tx_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) rtw89_debug(rtwdev, RTW89_DBG_TXRX, "C2H TX RPT: sn %d, tx_status %d, txcnt %d\n", sw_define, tx_status, txcnt); + + /* claim sw_define is not over size of tx_rpt->skbs[] */ + static_assert(hweight32(RTW89_MAX_TX_RPTS_MASK) == + hweight32(RTW89_C2H_MAC_TX_RPT_W12_SW_DEFINE_V2) && + hweight32(RTW89_MAX_TX_RPTS_MASK) == + hweight32(RTW89_C2H_MAC_TX_RPT_W2_SW_DEFINE)); + + scoped_guard(spinlock_irqsave, &tx_rpt->skb_lock) { + skb = tx_rpt->skbs[sw_define]; + + /* skip if no skb (normally shouldn't happen) */ + if (!skb) { + rtw89_debug(rtwdev, RTW89_DBG_TXRX, + "C2H TX RPT: no skb found in queue\n"); + return; + } + + skb_data = RTW89_TX_SKB_CB(skb); + + /* skip if TX attempt has failed and retry limit has not been + * reached yet + */ + if (tx_status != RTW89_TX_DONE && + txcnt != skb_data->tx_pkt_cnt_lmt) + return; + + tx_rpt->skbs[sw_define] = NULL; + rtw89_tx_rpt_tx_status(rtwdev, skb, tx_status); + } } static void diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index dfa85ade38ce..56751dd6e99b 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -1626,4 +1626,85 @@ int rtw89_mac_scan_offload(struct rtw89_dev *rtwdev, return ret; } + +static inline +void rtw89_tx_rpt_init(struct rtw89_dev *rtwdev, + struct rtw89_core_tx_request *tx_req) +{ + struct rtw89_tx_rpt *tx_rpt = &rtwdev->tx_rpt; + + if (!rtwdev->hci.tx_rpt_enabled) + return; + + tx_req->desc_info.report = true; + /* firmware maintains a 4-bit sequence number */ + tx_req->desc_info.sn = atomic_inc_return(&tx_rpt->sn) & + RTW89_MAX_TX_RPTS_MASK; + tx_req->desc_info.tx_cnt_lmt_en = true; + tx_req->desc_info.tx_cnt_lmt = 8; +} + +static inline +bool rtw89_is_tx_rpt_skb(struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + return info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS; +} + +static inline +void rtw89_tx_rpt_tx_status(struct rtw89_dev *rtwdev, struct sk_buff *skb, + u8 tx_status) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + ieee80211_tx_info_clear_status(info); + + if (tx_status == RTW89_TX_DONE) + info->flags |= IEEE80211_TX_STAT_ACK; + else + info->flags &= ~IEEE80211_TX_STAT_ACK; + + ieee80211_tx_status_irqsafe(rtwdev->hw, skb); +} + +static inline +void rtw89_tx_rpt_skb_add(struct rtw89_dev *rtwdev, struct sk_buff *skb) +{ + struct rtw89_tx_rpt *tx_rpt = &rtwdev->tx_rpt; + struct rtw89_tx_skb_data *skb_data; + u8 idx; + + skb_data = RTW89_TX_SKB_CB(skb); + idx = skb_data->tx_rpt_sn; + + scoped_guard(spinlock_irqsave, &tx_rpt->skb_lock) { + /* if skb having the similar seq number is still in the queue, + * this means the queue is overflowed - it isn't normal and + * should indicate firmware doesn't provide TX reports in time; + * report the old skb as dropped, we can't do much more here + */ + if (tx_rpt->skbs[idx]) + rtw89_tx_rpt_tx_status(rtwdev, tx_rpt->skbs[idx], + RTW89_TX_MACID_DROP); + tx_rpt->skbs[idx] = skb; + } +} + +static inline +void rtw89_tx_rpt_skbs_purge(struct rtw89_dev *rtwdev) +{ + struct rtw89_tx_rpt *tx_rpt = &rtwdev->tx_rpt; + struct sk_buff *skbs[RTW89_MAX_TX_RPTS]; + + scoped_guard(spinlock_irqsave, &tx_rpt->skb_lock) { + memcpy(skbs, tx_rpt->skbs, sizeof(tx_rpt->skbs)); + memset(tx_rpt->skbs, 0, sizeof(tx_rpt->skbs)); + } + + for (int i = 0; i < ARRAY_SIZE(skbs); i++) + if (skbs[i]) + rtw89_tx_rpt_tx_status(rtwdev, skbs[i], + RTW89_TX_MACID_DROP); +} #endif diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c index b7b981bac7bf..f54e00c3033e 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.c +++ b/drivers/net/wireless/realtek/rtw89/usb.c @@ -194,6 +194,15 @@ static void rtw89_usb_write_port_complete(struct urb *urb) skb_pull(skb, txdesc_size); + if (rtw89_is_tx_rpt_skb(skb)) { + if (urb->status == 0) + rtw89_tx_rpt_skb_add(rtwdev, skb); + else + rtw89_tx_rpt_tx_status(rtwdev, skb, + RTW89_TX_MACID_DROP); + continue; + } + info = IEEE80211_SKB_CB(skb); ieee80211_tx_info_clear_status(info); @@ -358,6 +367,7 @@ static int rtw89_usb_ops_tx_write(struct rtw89_dev *rtwdev, { struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info; struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); + struct rtw89_tx_skb_data *skb_data; struct sk_buff *skb = tx_req->skb; struct rtw89_txwd_body *txdesc; u32 txdesc_size; @@ -384,6 +394,12 @@ static int rtw89_usb_ops_tx_write(struct rtw89_dev *rtwdev, le32p_replace_bits(&txdesc->dword0, 1, RTW89_TXWD_BODY0_STF_MODE); + skb_data = RTW89_TX_SKB_CB(skb); + if (tx_req->desc_info.sn) + skb_data->tx_rpt_sn = tx_req->desc_info.sn; + if (tx_req->desc_info.tx_cnt_lmt) + skb_data->tx_pkt_cnt_lmt = tx_req->desc_info.tx_cnt_lmt; + skb_queue_tail(&rtwusb->tx_queue[desc_info->ch_dma], skb); return 0; @@ -672,6 +688,7 @@ static void rtw89_usb_ops_reset(struct rtw89_dev *rtwdev) struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); rtw89_usb_cancel_tx_bufs(rtwusb); + rtw89_tx_rpt_skbs_purge(rtwdev); } static int rtw89_usb_ops_start(struct rtw89_dev *rtwdev) @@ -962,6 +979,7 @@ int rtw89_usb_probe(struct usb_interface *intf, rtwdev->hci.ops = &rtw89_usb_ops; rtwdev->hci.type = RTW89_HCI_TYPE_USB; + rtwdev->hci.tx_rpt_enabled = true; ret = rtw89_usb_intf_init(rtwdev, intf); if (ret) {