mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-15 22:41:38 +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)
|
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 long time_left;
|
||||||
unsigned int cur;
|
unsigned int cur;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
cur = atomic_cmpxchg(&wait->cond, RTW89_WAIT_COND_IDLE, cond);
|
cur = atomic_cmpxchg(&wait->cond, RTW89_WAIT_COND_IDLE, cond);
|
||||||
if (cur != RTW89_WAIT_COND_IDLE)
|
if (cur != RTW89_WAIT_COND_IDLE)
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
|
||||||
time_left = wait_for_completion_timeout(cmpl, RTW89_WAIT_FOR_COND_TIMEOUT);
|
prep = kzalloc(sizeof(*prep), GFP_KERNEL);
|
||||||
if (time_left == 0) {
|
if (!prep) {
|
||||||
atomic_set(&wait->cond, RTW89_WAIT_COND_IDLE);
|
err = -ENOMEM;
|
||||||
return -ETIMEDOUT;
|
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)
|
if (wait->data.err)
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
return 0;
|
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,
|
void rtw89_complete_cond(struct rtw89_wait_info *wait, unsigned int cond,
|
||||||
const struct rtw89_completion_data *data)
|
const struct rtw89_completion_data *data)
|
||||||
{
|
{
|
||||||
|
struct rtw89_wait_response *resp;
|
||||||
unsigned int cur;
|
unsigned int cur;
|
||||||
|
|
||||||
|
guard(rcu)();
|
||||||
|
|
||||||
|
resp = rcu_dereference(wait->resp);
|
||||||
|
if (!resp)
|
||||||
|
return;
|
||||||
|
|
||||||
cur = atomic_cmpxchg(&wait->cond, cond, RTW89_WAIT_COND_IDLE);
|
cur = atomic_cmpxchg(&wait->cond, cond, RTW89_WAIT_COND_IDLE);
|
||||||
if (cur != cond)
|
if (cur != cond)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
wait->data = *data;
|
rtw89_complete_cond_resp(resp, data);
|
||||||
complete(&wait->completion);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void rtw89_core_ntfy_btc_event(struct rtw89_dev *rtwdev, enum rtw89_btc_hmsg event)
|
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];
|
u8 buf[RTW89_COMPLETION_BUF_SIZE];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct rtw89_wait_info {
|
struct rtw89_wait_response {
|
||||||
atomic_t cond;
|
struct rcu_head rcu_head;
|
||||||
struct completion completion;
|
struct completion completion;
|
||||||
struct rtw89_completion_data data;
|
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)
|
#define RTW89_WAIT_FOR_COND_TIMEOUT msecs_to_jiffies(100)
|
||||||
|
|
||||||
static inline void rtw89_init_wait(struct rtw89_wait_info *wait)
|
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);
|
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;
|
int ret;
|
||||||
|
|
||||||
|
lockdep_assert_wiphy(rtwdev->hw->wiphy);
|
||||||
|
|
||||||
ret = rtw89_h2c_tx(rtwdev, skb, false);
|
ret = rtw89_h2c_tx(rtwdev, skb, false);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
rtw89_err(rtwdev, "failed to send h2c\n");
|
rtw89_err(rtwdev, "failed to send h2c\n");
|
||||||
|
|||||||
Reference in New Issue
Block a user