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

[ Upstream commit bc2a5a12fa ]

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
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Zong-Zhe Yang
2025-09-15 14:53:43 +08:00
committed by Greg Kroah-Hartman
parent 336da44143
commit 72f1984246
3 changed files with 55 additions and 10 deletions

View File

@@ -4318,37 +4318,74 @@ void rtw89_core_update_beacon_work(struct work_struct *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)

View File

@@ -4391,17 +4391,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);
}

View File

@@ -7446,6 +7446,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");