From 49ff00af859a70eed20031de39b9c8e97dc9541d Mon Sep 17 00:00:00 2001 From: Ashish Kumar Dhanotiya Date: Mon, 25 Jan 2021 20:03:46 +0530 Subject: [PATCH] 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 --- core/hdd/src/wlan_hdd_main.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/core/hdd/src/wlan_hdd_main.c b/core/hdd/src/wlan_hdd_main.c index 7bd31a831d97..82b987efdad2 100644 --- a/core/hdd/src/wlan_hdd_main.c +++ b/core/hdd/src/wlan_hdd_main.c @@ -223,6 +223,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 + int wlan_start_ret_val; static DECLARE_COMPLETION(wlan_start_comp); static qdf_atomic_t wlan_hdd_state_fops_ref; @@ -5938,10 +5941,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)); } @@ -6070,7 +6080,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); @@ -6960,6 +6969,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); @@ -6974,8 +6984,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);