From e3807d4e506564046700852fc04d1bd0a9ab6a05 Mon Sep 17 00:00:00 2001 From: Shashikala Prabhu Date: Wed, 26 Sep 2018 12:23:25 +0530 Subject: [PATCH] qcacmn: Fix the nol timeout panic In macro TAILQ_FOREACH_SAFE, var and tvar point to current head and the next element respectively. If we unlock the spinlock inside the for loop and then lock it again, there is a chance (race) that the next element is pointed to by another thread of execution and both of them may try to remove the same element at the same time. This may lead to panic. Instead of using TAILQ_FOREACH_SAFE macro remove the element from the list one by one using a while loop from the head of the list. Do not lock the entire while loop, instead lock only during the removal of element from the list. This is required because we want to wait for the dfs_remove_from_nol timer to complete. But the wait should not be done from inside the lock because the same lock is used by the dfs_remove_from_nol timer. Change-Id: If820dbb1789b7fcfc33c133b3f90968377bfbf3c CRs-Fixed: 2322831 --- umac/dfs/core/src/misc/dfs_nol.c | 53 ++++++++++++++++---------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/umac/dfs/core/src/misc/dfs_nol.c b/umac/dfs/core/src/misc/dfs_nol.c index 8d203aa54ae1..9e317875aa44 100644 --- a/umac/dfs/core/src/misc/dfs_nol.c +++ b/umac/dfs/core/src/misc/dfs_nol.c @@ -116,22 +116,24 @@ static os_timer_func(dfs_nol_timeout) static void dfs_nol_elem_free_work_cb(void *context) { struct wlan_dfs *dfs = (struct wlan_dfs *)context; - struct dfs_nolelem *tmp_nol_entry, *nol_entry; + struct dfs_nolelem *nol_head; - WLAN_DFSNOL_LOCK(dfs); - if (!TAILQ_EMPTY(&dfs->dfs_nol_free_list)) - TAILQ_FOREACH_SAFE(nol_entry, - &dfs->dfs_nol_free_list, - nolelem_list, - tmp_nol_entry) { - TAILQ_REMOVE(&dfs->dfs_nol_free_list, - nol_entry, nolelem_list); + while (true) { + WLAN_DFSNOL_LOCK(dfs); + + nol_head = TAILQ_FIRST(&dfs->dfs_nol_free_list); + if (nol_head) { + TAILQ_REMOVE(&dfs->dfs_nol_free_list, nol_head, + nolelem_list); WLAN_DFSNOL_UNLOCK(dfs); - qdf_timer_free(&nol_entry->nol_timer); - WLAN_DFSNOL_LOCK(dfs); - qdf_mem_free(nol_entry); + + qdf_timer_free(&nol_head->nol_timer); + qdf_mem_free(nol_head); + } else { + WLAN_DFSNOL_UNLOCK(dfs); + break; } - WLAN_DFSNOL_UNLOCK(dfs); + } } void dfs_nol_timer_init(struct wlan_dfs *dfs) @@ -547,23 +549,22 @@ void dfs_nol_timer_cleanup(struct wlan_dfs *dfs) { struct dfs_nolelem *nol; - WLAN_DFSNOL_LOCK(dfs); - nol = dfs->dfs_nol; - while (nol) { - dfs->dfs_nol = nol->nol_next; - dfs->dfs_nol_count--; - /* - * Unlock is required so that when we sync with the - * nol_timeout timer we do not run into deadlock. - */ - WLAN_DFSNOL_UNLOCK(dfs); - qdf_timer_free(&nol->nol_timer); + while (true) { WLAN_DFSNOL_LOCK(dfs); - qdf_mem_free(nol); nol = dfs->dfs_nol; + if (nol) { + dfs->dfs_nol = nol->nol_next; + dfs->dfs_nol_count--; + WLAN_DFSNOL_UNLOCK(dfs); + + qdf_timer_free(&nol->nol_timer); + qdf_mem_free(nol); + } else { + WLAN_DFSNOL_UNLOCK(dfs); + break; + } } - WLAN_DFSNOL_UNLOCK(dfs); } void dfs_nol_workqueue_cleanup(struct wlan_dfs *dfs)