From: Anurag Chouhan Date: Fri, 8 Feb 2019 09:59:11 +0000 (+0530) Subject: icnss: Defer modem graceful shutdown until probe complete X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=1174076aa273369f86d2d46ad679dc0862c80db8;p=sagit-ice-cold%2Fkernel_xiaomi_msm8998.git icnss: Defer modem graceful shutdown until probe complete In case WLAN driver probe is in progress and modem graceful shutdown occurs and if modem shutdown request is sent just before the mode on request sent to firmware, firmware may end up in illegal memory access. To address this issue, modem notifier needs to be blocked needs for probe to complete or max 5 seconds timeout. CRs-Fixed: 2381846 Change-Id: I9e13a11c56059cb29e161c34df11de484f87ac5e Signed-off-by: Sandeep Singh --- diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index 8ada7af7fb79..fcfb649aedee 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -75,6 +75,8 @@ module_param(qmi_timeout, ulong, 0600); #define ICNSS_MAX_PROBE_CNT 2 +#define PROBE_TIMEOUT 5000 + #define icnss_ipc_log_string(_x...) do { \ if (icnss_ipc_log_context) \ ipc_log_string(icnss_ipc_log_context, _x); \ @@ -299,6 +301,7 @@ enum icnss_driver_state { ICNSS_FW_DOWN, ICNSS_DRIVER_UNLOADING, ICNSS_REJUVENATE, + ICNSS_DRIVER_LOADING, }; struct ce_irq_list { @@ -491,6 +494,7 @@ static struct icnss_priv { u8 requesting_sub_system; u16 line_number; char function_name[QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1]; + struct completion driver_probed; } *penv; #ifdef CONFIG_ICNSS_DEBUG @@ -2203,6 +2207,8 @@ static int icnss_call_driver_probe(struct icnss_priv *priv) icnss_hw_power_on(priv); + set_bit(ICNSS_DRIVER_LOADING, &priv->state); + reinit_completion(&penv->driver_probed); while (probe_cnt < ICNSS_MAX_PROBE_CNT) { ret = priv->ops->probe(&priv->pdev->dev); probe_cnt++; @@ -2212,9 +2218,13 @@ static int icnss_call_driver_probe(struct icnss_priv *priv) if (ret < 0) { icnss_pr_err("Driver probe failed: %d, state: 0x%lx, probe_cnt: %d\n", ret, priv->state, probe_cnt); + complete(&penv->driver_probed); + clear_bit(ICNSS_DRIVER_LOADING, &penv->state); goto out; } + complete(&penv->driver_probed); + clear_bit(ICNSS_DRIVER_LOADING, &penv->state); set_bit(ICNSS_DRIVER_PROBED, &priv->state); return 0; @@ -2350,6 +2360,8 @@ static int icnss_driver_event_register_driver(void *data) if (ret) goto out; + set_bit(ICNSS_DRIVER_LOADING, &penv->state); + reinit_completion(&penv->driver_probed); while (probe_cnt < ICNSS_MAX_PROBE_CNT) { ret = penv->ops->probe(&penv->pdev->dev); probe_cnt++; @@ -2359,9 +2371,13 @@ static int icnss_driver_event_register_driver(void *data) if (ret) { icnss_pr_err("Driver probe failed: %d, state: 0x%lx, probe_cnt: %d\n", ret, penv->state, probe_cnt); + clear_bit(ICNSS_DRIVER_LOADING, &penv->state); + complete(&penv->driver_probed); goto power_off; } + complete(&penv->driver_probed); + clear_bit(ICNSS_DRIVER_LOADING, &penv->state); set_bit(ICNSS_DRIVER_PROBED, &penv->state); return 0; @@ -2584,6 +2600,13 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb, if (code != SUBSYS_BEFORE_SHUTDOWN) return NOTIFY_OK; + if (code == SUBSYS_BEFORE_SHUTDOWN && !notif->crashed && + test_bit(ICNSS_DRIVER_LOADING, &priv->state)) { + if (!wait_for_completion_timeout(&priv->driver_probed, + PROBE_TIMEOUT)) + icnss_pr_err("wlan driver probe timeout\n"); + } + if (code == SUBSYS_BEFORE_SHUTDOWN && !notif->crashed) { ret = wlfw_send_modem_shutdown_msg(); if (ret) @@ -3981,6 +4004,9 @@ static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv) continue; case ICNSS_DRIVER_UNLOADING: seq_puts(s, "DRIVER UNLOADING"); + continue; + case ICNSS_DRIVER_LOADING: + seq_puts(s, "WLAN DRIVER LOADING"); } seq_printf(s, "UNKNOWN-%d", i); @@ -4652,6 +4678,8 @@ static int icnss_probe(struct platform_device *pdev) penv = priv; + init_completion(&priv->driver_probed); + icnss_pr_info("Platform driver probed successfully\n"); return 0; @@ -4674,6 +4702,8 @@ static int icnss_remove(struct platform_device *pdev) icnss_debugfs_destroy(penv); + complete_all(&penv->driver_probed); + icnss_modem_ssr_unregister_notifier(penv); destroy_ramdump_device(penv->msa0_dump_dev);