From eea229eb7daa2fb82b6d2d1d1a2f565b4a407f0e Mon Sep 17 00:00:00 2001 From: Jithender Miryala Date: Wed, 29 Jun 2022 12:31:53 +0530 Subject: [PATCH] qcacmn: Create NOL and CAC HRtimer in softIRQ context by using hrt_mode The NOL and CAC timeout callback function is divided into two parts: HardIRQ and workqueue. If NOL/CAC expires, the callback function runs in a HardIRQ context and the rest of the task runs in a workqueue to avoid deadlock issues. Issue: Workqueue tasks run in a low-level context. The task's execution might have been delayed. To fix this, run the "NOL" and "CAC" timer in softirq context. Use QDF_CONTEXT_TASKLET instead of QDF_CONTEXT_HARDWARE as a argument in qdf_hrtimer_init. If it is a "QDF_CONTEXT_TASKLET," the HR timer initialises with softIRQ mode(HRTIMER_MODE_SOFT) and runs in softRQ context when timer expires. Change-Id: I9e23a9dff1a9c4669bb85342b3ffd91bc46ebe95 CRs-Fixed: 3239417 --- qdf/linux/src/i_qdf_hrtimer.h | 25 ++++++++-------- umac/dfs/core/src/dfs.h | 25 ++-------------- umac/dfs/core/src/misc/dfs_cac.c | 14 ++------- umac/dfs/core/src/misc/dfs_nol.c | 49 ++++++++++---------------------- 4 files changed, 33 insertions(+), 80 deletions(-) diff --git a/qdf/linux/src/i_qdf_hrtimer.h b/qdf/linux/src/i_qdf_hrtimer.h index 173e5198cb..5e5be37952 100644 --- a/qdf/linux/src/i_qdf_hrtimer.h +++ b/qdf/linux/src/i_qdf_hrtimer.h @@ -76,8 +76,12 @@ static inline void __qdf_hrtimer_start(__qdf_hrtimer_data_t *timer, ktime_t interval, enum qdf_hrtimer_mode mode) { - enum hrtimer_mode hrt_mode = __qdf_hrtimer_get_mode(mode); + enum hrtimer_mode hrt_mode; + if (timer->ctx == QDF_CONTEXT_TASKLET) + mode |= HRTIMER_MODE_SOFT; + + hrt_mode = __qdf_hrtimer_get_mode(mode); hrtimer_start(&timer->u.hrtimer, interval, hrt_mode); } #else @@ -107,10 +111,7 @@ void __qdf_hrtimer_start(__qdf_hrtimer_data_t *timer, ktime_t interval, static inline int __qdf_hrtimer_cancel(__qdf_hrtimer_data_t *timer) { - if (timer->ctx == QDF_CONTEXT_HARDWARE) - return hrtimer_cancel(&timer->u.hrtimer); - - return 0; + return hrtimer_cancel(&timer->u.hrtimer); } #else static inline @@ -152,18 +153,18 @@ static inline void __qdf_hrtimer_init(__qdf_hrtimer_data_t *timer, enum qdf_context_mode ctx) { struct hrtimer *hrtimer = &timer->u.hrtimer; - enum hrtimer_mode hrt_mode = __qdf_hrtimer_get_mode(mode); + enum hrtimer_mode hrt_mode; timer->ctx = ctx; timer->callback = cback; timer->cb_ctx = timer; - if (timer->ctx == QDF_CONTEXT_HARDWARE) { - hrtimer_init(hrtimer, clock, hrt_mode); - hrtimer->function = __qdf_hrtimer_cb; - } else if (timer->ctx == QDF_CONTEXT_TASKLET) { - QDF_BUG(0); - } + if (timer->ctx == QDF_CONTEXT_TASKLET) + mode |= HRTIMER_MODE_SOFT; + + hrt_mode = __qdf_hrtimer_get_mode(mode); + hrtimer_init(hrtimer, clock, hrt_mode); + hrtimer->function = __qdf_hrtimer_cb; } #else static inline void __qdf_hrtimer_init(__qdf_hrtimer_data_t *timer, diff --git a/umac/dfs/core/src/dfs.h b/umac/dfs/core/src/dfs.h index ef206faac5..ffeb0d4601 100644 --- a/umac/dfs/core/src/dfs.h +++ b/umac/dfs/core/src/dfs.h @@ -765,7 +765,6 @@ struct dfs_state { * @nol_start_us: NOL start time in us. * @nol_timeout_ms: NOL timeout value in msec. * @nol_timer: Per element NOL timer. - * @nol_timer_completion_work: workqueue to process the nol timeout * @nol_next: Next element pointer. */ struct dfs_nolelem { @@ -776,7 +775,6 @@ struct dfs_nolelem { uint64_t nol_start_us; uint32_t nol_timeout_ms; qdf_hrtimer_data_t nol_timer; - qdf_work_t nol_timer_completion_work; struct dfs_nolelem *nol_next; }; @@ -1250,7 +1248,6 @@ struct wlan_dfs { qdf_work_t dfs_nol_elem_free_work; qdf_hrtimer_data_t dfs_cac_timer; - qdf_work_t dfs_cac_completion_work; qdf_timer_t dfs_cac_valid_timer; int dfs_cac_timeout_override; uint8_t dfs_enable:1, @@ -1355,7 +1352,6 @@ struct wlan_dfs_priv { * @cur_dfs_index: index of the current dfs object using the Agile Engine. * It is used to index struct wlan_dfs_priv dfs_priv[] array. * @dfs_precac_timer: agile precac timer - * @dfs_precac_completion_work: workqueue to process the precac timeout. * @dfs_precac_timer_running: precac timer running flag * @ocac_status: Off channel CAC complete status * @dfs_nol_ctx: dfs NOL data for all radios. @@ -1374,14 +1370,13 @@ struct dfs_soc_priv_obj { uint8_t num_dfs_privs; uint8_t cur_agile_dfs_index; qdf_hrtimer_data_t dfs_precac_timer; - qdf_work_t dfs_precac_completion_work; uint8_t dfs_precac_timer_running; bool precac_state_started; bool ocac_status; #endif struct dfsreq_nolinfo *dfs_psoc_nolinfo; #ifdef QCA_SUPPORT_ADFS_RCAC - qdf_timer_t dfs_rcac_timer; + qdf_hrtimer_data_t dfs_rcac_timer; #endif #ifdef QCA_SUPPORT_AGILE_DFS struct wlan_sm *dfs_agile_sm_hdl; @@ -2977,23 +2972,7 @@ void dfs_complete_deferred_tasks(struct wlan_dfs *dfs); * * Return: void. */ -void dfs_process_cac_completion(void *context); - -/** - * dfs_process_precac_completion() - Process DFS preCAC completion event. - * @dfs_soc_obj: Pointer to dfs_soc_obj object. - * - * Return: void. - */ -void dfs_process_precac_completion(void *context); - -/** - * dfs_process_noltimeout_completion() - Process NOL timeout completion event. - * @dfs_nolelem: Pointer to dfs_nolelem object. - * - * Return: void. - */ -void dfs_process_noltimeout_completion(void *context); +void dfs_process_cac_completion(struct wlan_dfs *dfs); #ifdef WLAN_DFS_TRUE_160MHZ_SUPPORT /** diff --git a/umac/dfs/core/src/misc/dfs_cac.c b/umac/dfs/core/src/misc/dfs_cac.c index 82846beb77..2315695c97 100644 --- a/umac/dfs/core/src/misc/dfs_cac.c +++ b/umac/dfs/core/src/misc/dfs_cac.c @@ -81,9 +81,8 @@ static void dfs_clear_nol_history_for_curchan(struct wlan_dfs *dfs) num_subchs, DFS_NOL_HISTORY_RESET); } -void dfs_process_cac_completion(void *context) +void dfs_process_cac_completion(struct wlan_dfs *dfs) { - struct wlan_dfs *dfs = (struct wlan_dfs *)context; enum phy_ch_width ch_width = CH_WIDTH_INVALID; uint16_t primary_chan_freq = 0, sec_chan_freq = 0; struct dfs_channel *dfs_curchan; @@ -176,7 +175,7 @@ dfs_cac_timeout(qdf_hrtimer_data_t *arg) if (dfs_is_hw_mode_switch_in_progress(dfs)) dfs->dfs_defer_params.is_cac_completed = true; else - qdf_sched_work(NULL, &dfs->dfs_cac_completion_work); + dfs_process_cac_completion(dfs); return QDF_HRTIMER_NORESTART; } @@ -191,11 +190,7 @@ void dfs_cac_timer_attach(struct wlan_dfs *dfs) dfs_cac_timeout, QDF_CLOCK_MONOTONIC, QDF_HRTIMER_MODE_REL, - QDF_CONTEXT_HARDWARE); - qdf_create_work(NULL, - &dfs->dfs_cac_completion_work, - dfs_process_cac_completion, - dfs); + QDF_CONTEXT_TASKLET); qdf_timer_init(NULL, &(dfs->dfs_cac_valid_timer), dfs_cac_valid_timeout, @@ -206,7 +201,6 @@ void dfs_cac_timer_attach(struct wlan_dfs *dfs) void dfs_cac_timer_reset(struct wlan_dfs *dfs) { qdf_hrtimer_cancel(&dfs->dfs_cac_timer); - qdf_flush_work(&dfs->dfs_cac_completion_work); dfs_get_override_cac_timeout(dfs, &(dfs->dfs_cac_timeout_override)); dfs_clear_cac_started_chan(dfs); @@ -215,8 +209,6 @@ void dfs_cac_timer_reset(struct wlan_dfs *dfs) void dfs_cac_timer_detach(struct wlan_dfs *dfs) { qdf_hrtimer_kill(&dfs->dfs_cac_timer); - qdf_flush_work(&dfs->dfs_cac_completion_work); - qdf_destroy_work(NULL, &dfs->dfs_cac_completion_work); qdf_timer_free(&dfs->dfs_cac_valid_timer); dfs->dfs_cac_valid = 0; } diff --git a/umac/dfs/core/src/misc/dfs_nol.c b/umac/dfs/core/src/misc/dfs_nol.c index 8ff8216beb..3f62793abc 100644 --- a/umac/dfs/core/src/misc/dfs_nol.c +++ b/umac/dfs/core/src/misc/dfs_nol.c @@ -72,9 +72,6 @@ static void dfs_nol_elem_free_work_cb(void *context) nolelem_list); WLAN_DFSNOL_UNLOCK(dfs); qdf_hrtimer_kill(&nol_head->nol_timer); - qdf_flush_work(&nol_head->nol_timer_completion_work); - qdf_destroy_work(NULL, - &nol_head->nol_timer_completion_work); qdf_mem_free(nol_head); } else { WLAN_DFSNOL_UNLOCK(dfs); @@ -153,18 +150,28 @@ static void dfs_nol_delete(struct wlan_dfs *dfs, } } -void dfs_process_noltimeout_completion(void *context) +/** + * dfs_remove_from_nol() - Remove the freq from NOL list. + * @arg: argument of the timer + * + * When NOL times out, this function removes the channel from NOL list. + */ +#ifdef CONFIG_CHAN_FREQ_API + +static enum qdf_hrtimer_restart_status +dfs_remove_from_nol(qdf_hrtimer_data_t *arg) { - struct dfs_nolelem *nol_arg; struct wlan_dfs *dfs; uint16_t delfreq; uint16_t delchwidth; uint8_t chan; + struct dfs_nolelem *nol_arg; - nol_arg = (struct dfs_nolelem *)context; + nol_arg = container_of(arg, struct dfs_nolelem, nol_timer); dfs = nol_arg->nol_dfs; delfreq = nol_arg->nol_freq; delchwidth = nol_arg->nol_chwidth; + /* Delete the given NOL entry. */ DFS_NOL_DELETE_CHAN_LOCKED(dfs, delfreq, delchwidth); @@ -194,7 +201,7 @@ void dfs_process_noltimeout_completion(void *context) * of, after VAP start. */ if (dfs_switch_to_postnol_chan_if_nol_expired(dfs)) - return; + return QDF_HRTIMER_NORESTART; /* * If BW Expand is enabled, check if the user configured channel is * available. If it is available, STOP the AGILE SM and Restart the @@ -221,25 +228,6 @@ void dfs_process_noltimeout_completion(void *context) utils_dfs_agile_sm_deliver_evt(dfs->dfs_pdev_obj, DFS_AGILE_SM_EV_AGILE_START); } -} - -/** - * dfs_remove_from_nol() - Remove the freq from NOL list. - * @arg: argument of the timer - * - * When NOL times out, this function removes the channel from NOL list. - */ -#ifdef CONFIG_CHAN_FREQ_API - -static enum qdf_hrtimer_restart_status -dfs_remove_from_nol(qdf_hrtimer_data_t *arg) -{ - struct dfs_nolelem *nol_arg; - - nol_arg = container_of(arg, struct dfs_nolelem, nol_timer); - - qdf_sched_work(NULL, &nol_arg->nol_timer_completion_work); - return QDF_HRTIMER_NORESTART; } #endif @@ -430,11 +418,7 @@ void dfs_nol_addchan(struct wlan_dfs *dfs, qdf_hrtimer_init(&elem->nol_timer, dfs_remove_from_nol, QDF_CLOCK_MONOTONIC, QDF_HRTIMER_MODE_REL, - QDF_CONTEXT_HARDWARE); - qdf_create_work(NULL, - &elem->nol_timer_completion_work, - dfs_process_noltimeout_completion, - elem); + QDF_CONTEXT_TASKLET); qdf_hrtimer_start(&elem->nol_timer, qdf_time_ms_to_ktime(dfs_nol_timeout * TIME_IN_MS), QDF_HRTIMER_MODE_REL); @@ -560,9 +544,6 @@ void dfs_nol_timer_cleanup(struct wlan_dfs *dfs) 1, DFS_NOL_RESET); qdf_hrtimer_kill(&nol->nol_timer); - qdf_flush_work(&nol->nol_timer_completion_work); - qdf_destroy_work(NULL, - &nol->nol_timer_completion_work); qdf_mem_free(nol); } else { WLAN_DFSNOL_UNLOCK(dfs);