Bläddra i källkod

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
Jithender Miryala 3 år sedan
förälder
incheckning
eea229eb7d

+ 13 - 12
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,

+ 2 - 23
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
 /**

+ 3 - 11
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;
 }

+ 15 - 34
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);