]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
wifi: rtw89: renew a completion for each H2C command waiting C2H event
authorZong-Zhe Yang <kevin_yang@realtek.com>
Mon, 15 Sep 2025 06:53:43 +0000 (14:53 +0800)
committerPing-Ke Shih <pkshih@realtek.com>
Thu, 18 Sep 2025 01:17:09 +0000 (09:17 +0800)
Logically before a waiting side which has already timed out turns the
atomic status back to idle, a completing side could still pass atomic
condition and call complete. It will make the following H2C commands,
waiting C2H events, get a completion unexpectedly early. Hence, renew
a completion for each H2C command waiting a C2H event.

Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250915065343.39023-1-pkshih@realtek.com
drivers/net/wireless/realtek/rtw89/core.c
drivers/net/wireless/realtek/rtw89/core.h
drivers/net/wireless/realtek/rtw89/fw.c

index e445fcd0218711c974f00726b78ebd61ace45106..759b9f850df2222d9b368ff75d84704f40aff773 100644 (file)
@@ -5340,37 +5340,74 @@ void rtw89_core_csa_beacon_work(struct wiphy *wiphy, struct wiphy_work *work)
 
 int rtw89_wait_for_cond(struct rtw89_wait_info *wait, unsigned int cond)
 {
-       struct completion *cmpl = &wait->completion;
+       struct rtw89_wait_response *prep;
        unsigned long time_left;
        unsigned int cur;
+       int err = 0;
 
        cur = atomic_cmpxchg(&wait->cond, RTW89_WAIT_COND_IDLE, cond);
        if (cur != RTW89_WAIT_COND_IDLE)
                return -EBUSY;
 
-       time_left = wait_for_completion_timeout(cmpl, RTW89_WAIT_FOR_COND_TIMEOUT);
+       prep = kzalloc(sizeof(*prep), GFP_KERNEL);
+       if (!prep) {
+               err = -ENOMEM;
+               goto reset;
+       }
+
+       init_completion(&prep->completion);
+
+       rcu_assign_pointer(wait->resp, prep);
+
+       time_left = wait_for_completion_timeout(&prep->completion,
+                                               RTW89_WAIT_FOR_COND_TIMEOUT);
        if (time_left == 0) {
-               atomic_set(&wait->cond, RTW89_WAIT_COND_IDLE);
-               return -ETIMEDOUT;
+               err = -ETIMEDOUT;
+               goto cleanup;
        }
 
+       wait->data = prep->data;
+
+cleanup:
+       rcu_assign_pointer(wait->resp, NULL);
+       kfree_rcu(prep, rcu_head);
+
+reset:
+       atomic_set(&wait->cond, RTW89_WAIT_COND_IDLE);
+
+       if (err)
+               return err;
+
        if (wait->data.err)
                return -EFAULT;
 
        return 0;
 }
 
+static void rtw89_complete_cond_resp(struct rtw89_wait_response *resp,
+                                    const struct rtw89_completion_data *data)
+{
+       resp->data = *data;
+       complete(&resp->completion);
+}
+
 void rtw89_complete_cond(struct rtw89_wait_info *wait, unsigned int cond,
                         const struct rtw89_completion_data *data)
 {
+       struct rtw89_wait_response *resp;
        unsigned int cur;
 
+       guard(rcu)();
+
+       resp = rcu_dereference(wait->resp);
+       if (!resp)
+               return;
+
        cur = atomic_cmpxchg(&wait->cond, cond, RTW89_WAIT_COND_IDLE);
        if (cur != cond)
                return;
 
-       wait->data = *data;
-       complete(&wait->completion);
+       rtw89_complete_cond_resp(resp, data);
 }
 
 void rtw89_core_ntfy_btc_event(struct rtw89_dev *rtwdev, enum rtw89_btc_hmsg event)
index 708132363da39a02bba1f25a1d95204325fb4486..2098c033b4612d1ca880abcf25bf5a1a56e53fae 100644 (file)
@@ -4550,17 +4550,23 @@ struct rtw89_completion_data {
        u8 buf[RTW89_COMPLETION_BUF_SIZE];
 };
 
+struct rtw89_wait_response {
+       struct rcu_head rcu_head;
+       struct completion completion;
+       struct rtw89_completion_data data;
+};
+
 struct rtw89_wait_info {
        atomic_t cond;
-       struct completion completion;
        struct rtw89_completion_data data;
+       struct rtw89_wait_response __rcu *resp;
 };
 
 #define RTW89_WAIT_FOR_COND_TIMEOUT msecs_to_jiffies(100)
 
 static inline void rtw89_init_wait(struct rtw89_wait_info *wait)
 {
-       init_completion(&wait->completion);
+       rcu_assign_pointer(wait->resp, NULL);
        atomic_set(&wait->cond, RTW89_WAIT_COND_IDLE);
 }
 
index 53d3591e239779fab303fc24106f48fb5f50498c..dbb94ed1f3c08733aa94eb6c5649cb4c013c42cf 100644 (file)
@@ -8788,6 +8788,8 @@ static int rtw89_h2c_tx_and_wait(struct rtw89_dev *rtwdev, struct sk_buff *skb,
 {
        int ret;
 
+       lockdep_assert_wiphy(rtwdev->hw->wiphy);
+
        ret = rtw89_h2c_tx(rtwdev, skb, false);
        if (ret) {
                rtw89_err(rtwdev, "failed to send h2c\n");