Bladeren bron

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
Ashish Kumar Dhanotiya 4 jaren geleden
bovenliggende
commit
cf5817a8bf
1 gewijzigde bestanden met toevoegingen van 17 en 5 verwijderingen
  1. 17 5
      core/hdd/src/wlan_hdd_main.c

+ 17 - 5
core/hdd/src/wlan_hdd_main.c

@@ -229,6 +229,9 @@
 /* PCIe gen speed change idle shutdown timer 100 milliseconds */
 #define HDD_PCIE_GEN_SPEED_CHANGE_TIMEOUT_MS (100)
 
+#define MAX_NET_DEV_REF_LEAK_ITERATIONS 10
+#define NET_DEV_REF_LEAK_ITERATION_SLEEP_TIME_MS 10
+
 #ifdef FEATURE_TSO
 #define TSO_FEATURE_FLAGS (NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_SG)
 #else
@@ -5904,10 +5907,17 @@ static char *net_dev_ref_debug_string_from_id(wlan_net_dev_ref_dbgid dbgid)
 
 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);
@@ -6040,7 +6050,6 @@ static void hdd_cleanup_adapter(struct hdd_context *hdd_ctx,
 	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);
 
@@ -6889,6 +6898,7 @@ void hdd_close_adapter(struct hdd_context *hdd_ctx,
 	 */
 	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);
 
@@ -6903,8 +6913,10 @@ void hdd_close_all_adapters(struct hdd_context *hdd_ctx, bool rtnl_held)
 
 	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);