qcacld-3.0: check net dev ref leak before removing adapter from list

Currently driver checks net dev ref leak for adapter, once it
removes adapter from the list which is not the correct way as
in some cases it is possible that one thread loops through the
the adapter list and uses one of the adapter and the other thread
deletes the adapter from the list, since the adapter is deleted
from the list, it's next and previous pointers will point to
itself which will lead first thread to fall into infinite loop.

To address above issue check net dev ref leak before removing
adapter from the adapter list so that it will make sure that
no other thread is holding the referene of this adapter.

Change-Id: I819458e9de8f016898e24bf3bb376acb657e8187
CRs-Fixed: 2854000
diff --git a/core/hdd/src/wlan_hdd_main.c b/core/hdd/src/wlan_hdd_main.c
index 15557f5..1d48ecf 100644
--- a/core/hdd/src/wlan_hdd_main.c
+++ b/core/hdd/src/wlan_hdd_main.c
@@ -212,6 +212,9 @@
 #define PANIC_ON_BUG_STR ""
 #endif
 
+#define MAX_NET_DEV_REF_LEAK_ITERATIONS 10
+#define NET_DEV_REF_LEAK_ITERATION_SLEEP_TIME_MS 10
+
 int wlan_start_ret_val;
 static DECLARE_COMPLETION(wlan_start_comp);
 static qdf_atomic_t wlan_hdd_state_fops_ref;
@@ -5534,10 +5537,17 @@
 
 static void hdd_check_for_net_dev_ref_leak(struct hdd_adapter *adapter)
 {
-	int id;
+	int i, id;
 
 	for (id = 0; id < NET_DEV_HOLD_ID_MAX; id++) {
-		if (adapter->net_dev_hold_ref_count[id]) {
+		for (i = 0; i < MAX_NET_DEV_REF_LEAK_ITERATIONS; i++) {
+			if (!adapter->net_dev_hold_ref_count[id])
+				break;
+			hdd_info("net_dev held for debug id %s",
+				 net_dev_ref_debug_string_from_id(id));
+			qdf_sleep(NET_DEV_REF_LEAK_ITERATION_SLEEP_TIME_MS);
+		}
+		if (i == MAX_NET_DEV_REF_LEAK_ITERATIONS) {
 			hdd_err("net_dev hold reference leak detected for debug id: %s",
 				net_dev_ref_debug_string_from_id(id));
 			QDF_BUG(0);
@@ -5670,7 +5680,6 @@
 	qdf_spinlock_destroy(&adapter->vdev_lock);
 	hdd_sta_info_deinit(&adapter->sta_info_list);
 	hdd_sta_info_deinit(&adapter->cache_sta_info_list);
-	hdd_check_for_net_dev_ref_leak(adapter);
 
 	wlan_hdd_debugfs_csr_deinit(adapter);
 	if (adapter->device_mode == QDF_STA_MODE)
@@ -6520,6 +6529,7 @@
 	 */
 	hdd_bus_bw_compute_timer_stop(hdd_ctx);
 
+	hdd_check_for_net_dev_ref_leak(adapter);
 	hdd_remove_adapter(hdd_ctx, adapter);
 	__hdd_close_adapter(hdd_ctx, adapter, rtnl_held);
 
@@ -6534,8 +6544,10 @@
 
 	hdd_enter();
 
-	while (QDF_IS_STATUS_SUCCESS(hdd_remove_front_adapter(hdd_ctx,
-							      &adapter))) {
+	while (QDF_IS_STATUS_SUCCESS(hdd_get_front_adapter(
+							hdd_ctx, &adapter))) {
+		hdd_check_for_net_dev_ref_leak(adapter);
+		hdd_remove_front_adapter(hdd_ctx, &adapter);
 		vdev_sync = osif_vdev_sync_unregister(adapter->dev);
 		if (vdev_sync)
 			osif_vdev_sync_wait_for_ops(vdev_sync);