wifi: rtw89: renew a completion for each H2C command waiting C2H event

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
This commit is contained in:
Zong-Zhe Yang
2025-09-18 09:17:09 +08:00
committed by Ping-Ke Shih
parent 5ff9e80de2
commit bc2a5a12fa
3 changed files with 55 additions and 10 deletions
+44 -7
View 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);
if (time_left == 0) {
atomic_set(&wait->cond, RTW89_WAIT_COND_IDLE);
return -ETIMEDOUT;
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) {
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)
+9 -3
View File
@@ -4550,17 +4550,23 @@ struct rtw89_completion_data {
u8 buf[RTW89_COMPLETION_BUF_SIZE];
};
struct rtw89_wait_info {
atomic_t cond;
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 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);
}
+2
View 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");