mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-06 01:49:46 +00:00
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:
committed by
Greg Kroah-Hartman
parent
336da44143
commit
72f1984246
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user