icnss2: Add is_idle_shutdown atomic variable to solve race condition

Currently, there might be a scenario where wlan host driver calls
idle_shutdown and at the same time icnss driver receives shutdown
notification for wpss. In this case Race condition might occur for
is_ssr variable which is shared among icnss_wpss_notifier_nb() api
and icnss_idle_shutdown() api.

To resolve this race condition, set is_idle_shutdown atomic variable
in icnss_idle_shutdown() api and check this variable before posting
ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER event.

Change-Id: I34b0c2de9899aeeb77063ea7c3029c3eeaf50a05
CRs-Fixed: 4055232
This commit is contained in:
Prateek Patil
2025-02-13 11:33:27 +05:30
committed by Ravindra Konda
parent 165b72bb58
commit b39ec00cf9
2 changed files with 21 additions and 8 deletions

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015-2020, 2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#define pr_fmt(fmt) "icnss2: " fmt
@@ -2904,7 +2904,8 @@ static void icnss_update_state_send_modem_shutdown(struct icnss_priv *priv,
atomic_set(&priv->is_shutdown, false);
if (!test_bit(ICNSS_PD_RESTART, &priv->state) &&
!test_bit(ICNSS_SHUTDOWN_DONE, &priv->state) &&
!test_bit(ICNSS_BLOCK_SHUTDOWN, &priv->state)) {
!test_bit(ICNSS_BLOCK_SHUTDOWN, &priv->state) &&
!atomic_read(&priv->is_idle_shutdown)) {
clear_bit(ICNSS_FW_READY, &priv->state);
icnss_driver_event_post(priv,
ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
@@ -3173,6 +3174,8 @@ static int icnss_wpss_ssr_register_notifier(struct icnss_priv *priv)
set_bit(ICNSS_SSR_REGISTERED, &priv->state);
atomic_set(&priv->is_idle_shutdown, false);
return ret;
}
@@ -4973,20 +4976,29 @@ EXPORT_SYMBOL(icnss_trigger_recovery);
int icnss_idle_shutdown(struct device *dev)
{
struct icnss_priv *priv = dev_get_drvdata(dev);
int ret = 0;
if (!priv) {
icnss_pr_err("Invalid drvdata: dev %pK", dev);
return -EINVAL;
ret = -EINVAL;
goto out;
}
atomic_set(&priv->is_idle_shutdown, true);
if (priv->is_ssr || test_bit(ICNSS_PDR, &priv->state) ||
test_bit(ICNSS_REJUVENATE, &priv->state)) {
icnss_pr_err("SSR/PDR is already in-progress during idle shutdown\n");
return -EBUSY;
test_bit(ICNSS_REJUVENATE, &priv->state) || atomic_read(&priv->is_shutdown)) {
icnss_pr_err("SSR/PDR/Shutdown is already in-progress during idle shutdown\n");
atomic_set(&priv->is_idle_shutdown, false);
ret = -EBUSY;
goto out;
}
return icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_IDLE_SHUTDOWN,
ret = icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_IDLE_SHUTDOWN,
ICNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL);
atomic_set(&priv->is_idle_shutdown, false);
out:
return ret;
}
EXPORT_SYMBOL(icnss_idle_shutdown);

View File

@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2017-2020, 2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2023-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __MAIN_H__
@@ -572,6 +572,7 @@ struct icnss_priv {
struct kobject *icnss_kobject;
struct rproc *rproc;
atomic_t is_shutdown;
atomic_t is_idle_shutdown;
u32 qdss_mem_seg_len;
struct icnss_fw_mem qdss_mem[QMI_WLFW_MAX_NUM_MEM_SEG_V01];
struct icnss_fw_mem phy_ucode_mem;