From 0fe53af86212bd9fab635c526efff001e0584102 Mon Sep 17 00:00:00 2001 From: Sandeep Singh Date: Fri, 8 Feb 2019 15:29:11 +0530 Subject: [PATCH 1/2] 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 --- drivers/soc/qcom/icnss.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index fae06806b7db..f1eeb66ed80a 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) @@ -3980,6 +4003,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); @@ -4651,6 +4677,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; @@ -4673,6 +4701,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); From ab8f63420ca818ca92a85bf13db3711d2c936d26 Mon Sep 17 00:00:00 2001 From: Sandeep Singh Date: Fri, 8 Feb 2019 15:46:53 +0530 Subject: [PATCH 2/2] icnss: Add Api to Block/Unblock modem shutdown Add API to Block/Unblock modem graceful shutdown. Change-Id: I69b061fc7d25762b2c36d9590802addfc170f91f Signed-off-by: Sandeep Singh --- drivers/soc/qcom/icnss.c | 49 ++++++++++++++++++++++++---------------- include/soc/qcom/icnss.h | 9 ++------ 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index f1eeb66ed80a..454a897c19ab 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -301,7 +301,7 @@ enum icnss_driver_state { ICNSS_FW_DOWN, ICNSS_DRIVER_UNLOADING, ICNSS_REJUVENATE, - ICNSS_DRIVER_LOADING, + ICNSS_BLOCK_SHUTDOWN, }; struct ce_irq_list { @@ -494,7 +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; + struct completion unblock_shutdown; } *penv; #ifdef CONFIG_ICNSS_DEBUG @@ -1182,6 +1182,21 @@ bool icnss_is_fw_ready(void) } EXPORT_SYMBOL(icnss_is_fw_ready); +void icnss_block_shutdown(bool status) +{ + if (!penv) + return; + + if (status) { + set_bit(ICNSS_BLOCK_SHUTDOWN, &penv->state); + reinit_completion(&penv->unblock_shutdown); + } else { + clear_bit(ICNSS_BLOCK_SHUTDOWN, &penv->state); + complete(&penv->unblock_shutdown); + } +} +EXPORT_SYMBOL(icnss_block_shutdown); + bool icnss_is_fw_down(void) { if (!penv) @@ -2207,8 +2222,7 @@ 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); + icnss_block_shutdown(true); while (probe_cnt < ICNSS_MAX_PROBE_CNT) { ret = priv->ops->probe(&priv->pdev->dev); probe_cnt++; @@ -2218,13 +2232,11 @@ 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); + icnss_block_shutdown(false); goto out; } - complete(&penv->driver_probed); - clear_bit(ICNSS_DRIVER_LOADING, &penv->state); + icnss_block_shutdown(false); set_bit(ICNSS_DRIVER_PROBED, &priv->state); return 0; @@ -2360,8 +2372,7 @@ 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); + icnss_block_shutdown(true); while (probe_cnt < ICNSS_MAX_PROBE_CNT) { ret = penv->ops->probe(&penv->pdev->dev); probe_cnt++; @@ -2371,13 +2382,11 @@ 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); + icnss_block_shutdown(false); goto power_off; } - complete(&penv->driver_probed); - clear_bit(ICNSS_DRIVER_LOADING, &penv->state); + icnss_block_shutdown(false); set_bit(ICNSS_DRIVER_PROBED, &penv->state); return 0; @@ -2601,8 +2610,8 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb, 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, + test_bit(ICNSS_BLOCK_SHUTDOWN, &priv->state)) { + if (!wait_for_completion_timeout(&priv->unblock_shutdown, PROBE_TIMEOUT)) icnss_pr_err("wlan driver probe timeout\n"); } @@ -4004,8 +4013,8 @@ static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv) case ICNSS_DRIVER_UNLOADING: seq_puts(s, "DRIVER UNLOADING"); continue; - case ICNSS_DRIVER_LOADING: - seq_puts(s, "WLAN DRIVER LOADING"); + case ICNSS_BLOCK_SHUTDOWN: + seq_puts(s, "BLOCK SHUTDOWN"); } seq_printf(s, "UNKNOWN-%d", i); @@ -4677,7 +4686,7 @@ static int icnss_probe(struct platform_device *pdev) penv = priv; - init_completion(&priv->driver_probed); + init_completion(&priv->unblock_shutdown); icnss_pr_info("Platform driver probed successfully\n"); @@ -4701,7 +4710,7 @@ static int icnss_remove(struct platform_device *pdev) icnss_debugfs_destroy(penv); - complete_all(&penv->driver_probed); + complete_all(&penv->unblock_shutdown); icnss_modem_ssr_unregister_notifier(penv); diff --git a/include/soc/qcom/icnss.h b/include/soc/qcom/icnss.h index 85ec8beb3157..010f29db8d48 100644 --- a/include/soc/qcom/icnss.h +++ b/include/soc/qcom/icnss.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -157,10 +157,5 @@ extern bool icnss_is_rejuvenate(void); extern int icnss_set_wlan_mac_address(const u8 *in, const uint32_t len); extern u8 *icnss_get_wlan_mac_address(struct device *dev, uint32_t *num); extern int icnss_trigger_recovery(struct device *dev); -extern void cnss_set_cc_source(enum cnss_cc_src cc_source); -extern enum cnss_cc_src cnss_get_cc_source(void); -extern int icnss_get_driver_load_cnt(void); -extern void icnss_increment_driver_load_cnt(void); -extern void icnss_set_cc_source(enum cnss_cc_src cc_source); -extern enum cnss_cc_src icnss_get_cc_source(void); +extern void icnss_block_shutdown(bool status); #endif /* _ICNSS_WLAN_H_ */