Jelajahi Sumber

qcacmn: Fix DFS nol memory leak

Problem:- If the driver is unloaded before a nol timer fires, the unload
function frees the NOl element. But the memory associated with the nol timer
argument which has a one-to-one mapping with nol still remains in the system
since the nol timer argument memory is freed in the timer function.

Solution:-
1)Instead of allocating a separate structure as input to the timer function
use the NOL element itself as input since NOL element already has the
information needed by the timer function.
2)A NOL element for which  the timer is yet to fire is freed
by the driver unload (dfs_nol_timer_cleanup).
3)When dfs_nol_timer_cleanup is about to free a NOL element the corresponding
timer may be already running. Therefore, it is required to sync/wait for the
timer to finish before the NOL element can be freed. If we do not wait and
free the NOL element the timer may access an invalid memory location.

Change-Id: I4f37b66777491d92a51ff1ffc2651af1ccf5de29
CRs-Fixed: 2045993
Abhijit Pradhan 8 tahun lalu
induk
melakukan
0c80b53ac7
2 mengubah file dengan 10 tambahan dan 29 penghapusan
  1. 2 11
      umac/dfs/core/src/dfs.h
  2. 8 18
      umac/dfs/core/src/misc/dfs_nol.c

+ 2 - 11
umac/dfs/core/src/dfs.h

@@ -475,6 +475,7 @@ struct dfs_state {
 
 /**
  * struct dfs_nolelem - DFS NOL element.
+ * @nol_dfs           Back pointer to dfs object.
  * @nol_freq:         Centre frequency.
  * @nol_chwidth:      Event width (MHz).
  * @nol_start_ticks:  NOL start time in OS ticks.
@@ -483,6 +484,7 @@ struct dfs_state {
  * @nol_next:         Next element pointer.
  */
 struct dfs_nolelem {
+	struct wlan_dfs *nol_dfs;
 	uint32_t       nol_freq;
 	uint32_t       nol_chwidth;
 	unsigned long  nol_start_ticks;
@@ -491,17 +493,6 @@ struct dfs_nolelem {
 	struct dfs_nolelem *nol_next;
 } qdf_packed;
 
-/**
- * struct dfs_nol_timer_arg - DFS NOL timer arguments.
- * @dfs:        Object of wlan_dfs structure.
- * @delfreq:    Freq to delete.
- * @delchwidth: Channel width to delete.
- */
-struct dfs_nol_timer_arg {
-	struct wlan_dfs *dfs;
-	uint16_t       delfreq;
-	uint16_t       delchwidth;
-};
 
 /**
  * struct dfs_info - DFS Info.

+ 8 - 18
umac/dfs/core/src/misc/dfs_nol.c

@@ -101,17 +101,17 @@ static void dfs_nol_delete(struct wlan_dfs *dfs,
  */
 static os_timer_func(dfs_remove_from_nol)
 {
-	struct dfs_nol_timer_arg *nol_arg;
+	struct dfs_nolelem *nol_arg;
 	struct wlan_dfs *dfs;
 	uint16_t delfreq;
 	uint16_t delchwidth;
 	uint8_t chan;
 
-	OS_GET_TIMER_ARG(nol_arg, struct dfs_nol_timer_arg *);
+	OS_GET_TIMER_ARG(nol_arg, struct dfs_nolelem *);
 
-	dfs = nol_arg->dfs;
-	delfreq = nol_arg->delfreq;
-	delchwidth = nol_arg->delchwidth;
+	dfs = nol_arg->nol_dfs;
+	delfreq = nol_arg->nol_freq;
+	delchwidth = nol_arg->nol_chwidth;
 
 	/* Delete the given NOL entry. */
 	dfs_nol_delete(dfs, delfreq, delchwidth);
@@ -126,7 +126,6 @@ static os_timer_func(dfs_remove_from_nol)
 	utils_dfs_reg_update_nol_ch(dfs->dfs_pdev_obj,
 			(uint8_t *)&chan, 1, DFS_NOL_RESET);
 	dfs_save_nol(dfs->dfs_pdev_obj);
-	qdf_mem_free(nol_arg);
 }
 
 void dfs_print_nol(struct wlan_dfs *dfs)
@@ -256,7 +255,6 @@ void dfs_nol_addchan(struct wlan_dfs *dfs,
 #define TIME_IN_MS 1000
 #define TIME_IN_US (TIME_IN_MS * 1000)
 	struct dfs_nolelem *nol, *elem, *prev;
-	struct dfs_nol_timer_arg *dfs_nol_arg;
 	/* For now, assume all events are 20MHz wide. */
 	int ch_width = 20;
 
@@ -292,12 +290,7 @@ void dfs_nol_addchan(struct wlan_dfs *dfs,
 	if (elem == NULL)
 		goto bad;
 
-	dfs_nol_arg = (struct dfs_nol_timer_arg *)qdf_mem_malloc(
-			sizeof(struct dfs_nol_timer_arg));
-	if (dfs_nol_arg == NULL) {
-		qdf_mem_free(elem);
-		goto bad;
-	}
+	elem->nol_dfs = dfs;
 	elem->nol_freq = freq;
 	elem->nol_chwidth = ch_width;
 	elem->nol_start_ticks = qdf_system_ticks();
@@ -309,13 +302,10 @@ void dfs_nol_addchan(struct wlan_dfs *dfs,
 		/* This is the first element in the NOL. */
 		dfs->dfs_nol = elem;
 	}
-	dfs_nol_arg->dfs = dfs;
-	dfs_nol_arg->delfreq = elem->nol_freq;
-	dfs_nol_arg->delchwidth = elem->nol_chwidth;
 
 	qdf_timer_init(NULL,
 			&elem->nol_timer, dfs_remove_from_nol,
-			dfs_nol_arg, QDF_TIMER_TYPE_WAKE_APPS);
+			elem, QDF_TIMER_TYPE_WAKE_APPS);
 	OS_SET_TIMER(&elem->nol_timer, dfs_nol_timeout * TIME_IN_MS);
 
 	/* Update the NOL counter. */
@@ -414,7 +404,7 @@ void dfs_nol_timer_cleanup(struct wlan_dfs *dfs)
 	struct dfs_nolelem *nol = dfs->dfs_nol, *prev;
 
 	while (nol) {
-		qdf_timer_stop(&nol->nol_timer);
+		qdf_timer_sync_cancel(&nol->nol_timer);
 		nol = nol->nol_next;
 	}