فهرست منبع

qcacld-3.0: skip debug domain transition if system is rebooting

Considering the case as below:
System shutdown comes when ndo_open is waiting for cnss power
up complete.
cnss shutdown handler completes all of the completions, and so
ndo_open continues.
while at the same time, cnss driver is processing firmware ready
event and trying to start cld modules, which will change debug
domain from INIT to ACTIVE, then it may hit debug domain mismatch
assertion:
It allocates some memory in ndo_open context when debug domain is
INIT, but when trying to free the memory, the debug domain is ACTIVE.

To fix this issue, when system reboot is in progress, skip debug
domain transition, and also skip modules start to avoid state mismatch
between cnss driver and CLD driver.

Change-Id: Ibc411e13e48269c66ec71aba6304578c9049f798
CRs-Fixed: 3470170
Yu Wang 2 سال پیش
والد
کامیت
2f754dba7c

+ 27 - 0
core/cds/inc/cds_api.h

@@ -69,6 +69,7 @@
  * @CDS_DRIVER_STATE_FW_READY: Driver Firmware ready
  * @CDS_DRIVER_STATE_MODULE_STOP: Module stop in progress or done.
  * @CDS_DRIVER_STATE_ASSERTING_TARGET: Driver assert target in progress.
+ * @CDS_DRIVER_STATE_SYS_REBOOTING: System reboot in progress.
  */
 enum cds_driver_state {
 	CDS_DRIVER_STATE_UNINITIALIZED          = 0,
@@ -80,6 +81,7 @@ enum cds_driver_state {
 	CDS_DRIVER_STATE_FW_READY               = BIT(5),
 	CDS_DRIVER_STATE_MODULE_STOP            = BIT(6),
 	CDS_DRIVER_STATE_ASSERTING_TARGET       = BIT(7),
+	CDS_DRIVER_STATE_SYS_REBOOTING          = BIT(8),
 };
 
 /**
@@ -326,6 +328,31 @@ static inline bool cds_is_target_asserting(void)
 	return __CDS_IS_DRIVER_STATE(state, CDS_DRIVER_STATE_ASSERTING_TARGET);
 }
 
+/**
+ * cds_set_sys_rebooting() - Set system reboot in progress
+ *
+ * Return: none
+ */
+void cds_set_sys_rebooting(void);
+
+/**
+ * cds_sys_reboot_protect() - Require the lock for system reboot and get
+ * system rebooting state
+ *
+ * cds_sys_reboot_protect() and cds_sys_reboot_unprotect() MUST be used
+ * in pair.
+ *
+ * Return: true if system is rebooting, false otherwise
+ */
+bool cds_sys_reboot_protect(void);
+
+/**
+ * cds_sys_reboot_unprotect() - Release the lock for system reboot
+ *
+ * Return: none
+ */
+void cds_sys_reboot_unprotect(void);
+
 /**
  * cds_init() - Initialize CDS
  *

+ 3 - 0
core/cds/inc/cds_sched.h

@@ -218,6 +218,9 @@ struct cds_context {
 	qdf_work_t cds_recovery_work;
 	qdf_workqueue_t *cds_recovery_wq;
 	enum qdf_hang_reason recovery_reason;
+
+	/* To protect bit(CDS_DRIVER_STATE_SYS_REBOOTING) of driver_state */
+	qdf_mutex_t sys_reboot_lock;
 };
 
 /*---------------------------------------------------------------------------

+ 52 - 1
core/cds/src/cds_api.c

@@ -253,16 +253,64 @@ static void cds_update_recovery_reason(enum qdf_hang_reason recovery_reason)
 	gp_cds_context->recovery_reason = recovery_reason;
 }
 
+/**
+ * cds_sys_reboot_lock_init() - Create lock for system reboot
+ *
+ * Return: QDF_STATUS_SUCCESS if the lock was created and an error on failure
+ */
+static QDF_STATUS cds_sys_reboot_lock_init(void)
+{
+	return qdf_mutex_create(&gp_cds_context->sys_reboot_lock);
+}
+
+/**
+ * cds_sys_reboot_lock_deinit() - destroy lock for system reboot
+ *
+ * Return: none
+ */
+static void cds_sys_reboot_lock_deinit(void)
+{
+	qdf_mutex_destroy(&gp_cds_context->sys_reboot_lock);
+}
+
+void cds_set_sys_rebooting(void)
+{
+	qdf_mutex_acquire(&gp_cds_context->sys_reboot_lock);
+	cds_set_driver_state(CDS_DRIVER_STATE_SYS_REBOOTING);
+	qdf_mutex_release(&gp_cds_context->sys_reboot_lock);
+}
+
+bool cds_sys_reboot_protect(void)
+{
+	enum cds_driver_state state;
+
+	qdf_mutex_acquire(&gp_cds_context->sys_reboot_lock);
+
+	state = cds_get_driver_state();
+	return __CDS_IS_DRIVER_STATE(state, CDS_DRIVER_STATE_SYS_REBOOTING);
+}
+
+void cds_sys_reboot_unprotect(void)
+{
+	qdf_mutex_release(&gp_cds_context->sys_reboot_lock);
+}
+
 QDF_STATUS cds_init(void)
 {
 	QDF_STATUS status;
 
 	gp_cds_context = &g_cds_context;
 
+	status = cds_sys_reboot_lock_init();
+	if (QDF_IS_STATUS_ERROR(status)) {
+		cds_err("Failed to init sys reboot lock; status:%u", status);
+		goto deinit;
+	}
+
 	status = cds_recovery_work_init();
 	if (QDF_IS_STATUS_ERROR(status)) {
 		cds_err("Failed to init recovery work; status:%u", status);
-		goto deinit;
+		goto destroy_lock;
 	}
 
 	cds_ssr_protect_init();
@@ -283,6 +331,8 @@ QDF_STATUS cds_init(void)
 
 	return QDF_STATUS_SUCCESS;
 
+destroy_lock:
+	cds_sys_reboot_lock_deinit();
 deinit:
 	gp_cds_context = NULL;
 	qdf_mem_zero(&g_cds_context, sizeof(g_cds_context));
@@ -316,6 +366,7 @@ void cds_deinit(void)
 	/* currently, no ssr_protect_deinit */
 
 	cds_recovery_work_deinit();
+	cds_sys_reboot_lock_deinit();
 
 	gp_cds_context = NULL;
 	qdf_mem_zero(&g_cds_context, sizeof(g_cds_context));

+ 4 - 0
core/hdd/src/wlan_hdd_driver_ops.c

@@ -2180,6 +2180,10 @@ wlan_hdd_pld_uevent(struct device *dev, struct pld_uevent_data *event_data)
 		if (event_data->bus_data.etype == PLD_BUS_EVENT_PCIE_LINK_DOWN)
 			host_log_device_status(WLAN_STATUS_BUS_EXCEPTION);
 		break;
+	case PLD_SYS_REBOOT:
+		hdd_info("Received system reboot");
+		cds_set_sys_rebooting();
+		break;
 	default:
 		/* other events intentionally not handled */
 		hdd_debug("Received uevent %d", event_data->uevent);

+ 33 - 4
core/hdd/src/wlan_hdd_main.c

@@ -3855,7 +3855,34 @@ static void hdd_check_for_leaks(struct hdd_context *hdd_ctx, bool is_ssr)
 	qdf_mem_check_for_leaks();
 }
 
-#define hdd_debug_domain_set(domain) qdf_debug_domain_set(domain)
+/**
+ * hdd_debug_domain_set() - Set qdf debug domain
+ * @domain: debug domain to be set
+ *
+ * In the scenario of system reboot, it may have thread accessing debug domain
+ * for memory allocation/free, other than the one trying to change it.
+ * If debug domain is changed after a memory allocation but before the free,
+ * it will hit debug domain mismatch assertion in memory free.
+ * To avoid such assertion, skip debug domain transition if system reboot is
+ * in progress.
+ *
+ * Return: 0 if the specified debug domain has been set, -EBUSY otherwise
+ */
+static int hdd_debug_domain_set(enum qdf_debug_domain domain)
+{
+	int ret = 0;
+
+	if (cds_sys_reboot_protect()) {
+		hdd_info("System is rebooting, skip debug domain transition");
+		ret = -EBUSY;
+	} else {
+		qdf_debug_domain_set(domain);
+	}
+
+	cds_sys_reboot_unprotect();
+
+	return ret;
+}
 #else
 static void hdd_check_for_objmgr_peer_leaks(struct wlan_objmgr_psoc *psoc)
 {
@@ -3922,7 +3949,8 @@ static void hdd_check_for_leaks(struct hdd_context *hdd_ctx, bool is_ssr)
 {
 	hdd_check_for_objmgr_leaks(hdd_ctx);
 }
-#define hdd_debug_domain_set(domain)
+
+#define hdd_debug_domain_set(domain) 0
 #endif /* CONFIG_LEAK_DETECTION */
 
 #ifdef FEATURE_WLAN_AP_AP_ACS_OPTIMIZE
@@ -4425,8 +4453,9 @@ int hdd_wlan_start_modules(struct hdd_context *hdd_ctx, bool reinit)
 		fallthrough;
 	case DRIVER_MODULES_CLOSED:
 		hdd_nofl_debug("Wlan transitioning (CLOSED -> ENABLED)");
-
-		hdd_debug_domain_set(QDF_DEBUG_DOMAIN_ACTIVE);
+		ret = hdd_debug_domain_set(QDF_DEBUG_DOMAIN_ACTIVE);
+		if (ret)
+			goto release_lock;
 
 		if (!reinit && !unint) {
 			ret = pld_power_on(qdf_dev->dev);

+ 2 - 0
core/pld/inc/pld_common.h

@@ -203,6 +203,7 @@ struct pld_platform_cap {
  * @PLD_FW_HANG_EVENT: firmware update hang event
  * @PLD_BUS_EVENT: update bus/link event
  * @PLD_SMMU_FAULT: SMMU fault
+ * @PLD_SYS_REBOOT: system is rebooting
  */
 enum pld_uevent {
 	PLD_FW_DOWN,
@@ -211,6 +212,7 @@ enum pld_uevent {
 	PLD_FW_HANG_EVENT,
 	PLD_BUS_EVENT,
 	PLD_SMMU_FAULT,
+	PLD_SYS_REBOOT,
 };
 
 /**

+ 3 - 0
core/pld/src/pld_pcie.c

@@ -296,6 +296,9 @@ static void pld_pcie_uevent(struct pci_dev *pdev, uint32_t status)
 	case CNSS_FW_DOWN:
 		data.uevent = PLD_FW_DOWN;
 		break;
+	case CNSS_SYS_REBOOT:
+		data.uevent = PLD_SYS_REBOOT;
+		break;
 	default:
 		goto out;
 	}