Browse Source

qca-wifi: Check if channel is already marked before marking as CAC done

Consider the case where preCAC is running on channel 120 (20MHz)
bandwidth and 116 (20MHz) channel is already preCACed. One possible tree
structure, with the scenario mentioned above, is shown below:

122(1,0)
 |
 |------- 118(1,0)
 |        |
 |        |------- 116(1,0)
 |        |
 |        |------- 120(0,0)
 |
 |------- 126(0,0)
 |        |
 |        |------- 124(0,0)
 |        |
 |        |------- 128(0,0)

where 122 and 118 have 1 subchannel (116) as CAC done. While the preCAC
is running on 120(20Mhz), the preCAC is restarted with a new channel
of 40MHz bandwidth. If the new channel picked is 118(40Mhz) in which 116
is already CACed, once the preCAC is completed on 118 (40Mhz),
the entire band (with both 20MHz channels) will be marked as CAC done.

In the API "dfs_mark_tree_node_as_cac_done", the number of CACed
subchannel of the parent node is incremented without checking if the
child has been marked. In the above case, after preCAC is completed
on channel 118, each 20 MHz channels in channel 118 (116 and 120) are
marked individually in this API.
While marking the tree for preCAC completion on the subchannel 116,
the number of CACed subchannels is incremented to 2 for 122 and 118, but
124 is already marked. While marking the tree for preCAC completion on
the other subchannel (120), the CACed subchannels count is incremented
to 3 for 122 and since it cannot increment the subchannel count to more
than 2 for 118, it returns, leaving 128 unmarked. The preCAC tree
structure will now look like:

122(3,0)
 |
 |------- 118(2,0)
 |        |
 |        |------- 116(1,0)
 |        |
 |        |------- 120(0,0)
 |
 |------- 126(0,0)
 |        |
 |        |------- 124(0,0)
 |        |
 |        |------- 128(0,0)

which is wrong.

To fix the above problem, check if a tree node is already marked as
CAC done and only then increment their parent's CACed subchannels count.
This issue is not seen for other cases (e.g. marking NOL channels), since
preCAC can be performed on the same channel again (incase of different
bandwidths) whereas NOL marking is unique.

Change-Id: Ie683a011a86233dd2c1ff8e21aa78638a1d63c2a
CRs-Fixed: 2520790
Vignesh Mohan 5 years ago
parent
commit
aea46f34e4
1 changed files with 38 additions and 10 deletions
  1. 38 10
      umac/dfs/core/src/misc/dfs_zero_cac.c

+ 38 - 10
umac/dfs/core/src/misc/dfs_zero_cac.c

@@ -571,6 +571,29 @@ void dfs_prepare_agile_precac_chan(struct wlan_dfs *dfs)
 }
 #endif
 
+/* dfs_is_tree_node_marked_as_cac() - Check if preCAC BSTree node is
+ * marked as CAC.
+ * @root: Pointer to root node of the preCAC BSTree.
+ * @channel: 20MHz channel to be checked if marked as CAC done already.
+ *
+ * Return: True if already marked, else false.
+ */
+static bool
+dfs_is_tree_node_marked_as_cac(struct precac_tree_node *root,
+				  uint8_t channel)
+{
+	struct precac_tree_node *curr_node = root;
+
+	while (curr_node) {
+		if (!curr_node->n_caced_subchs)
+			return false;
+		if (curr_node->ch_ieee == channel)
+			return curr_node->n_caced_subchs;
+		curr_node = dfs_descend_precac_tree(curr_node, channel);
+	}
+	return false;
+}
+
 /* dfs_mark_tree_node_as_cac_done() - Mark the preCAC BSTree node as CAC done.
  * @dfs:          Pointer to WLAN DFS structure.
  * @precac_entry: Precac_list entry pointer.
@@ -592,18 +615,23 @@ dfs_mark_tree_node_as_cac_done(struct wlan_dfs *dfs,
 	}
 
 	curr_node = precac_entry->tree_root;
+
+	/**
+	 * Check if the channel is already marked and return if true.
+	 * This will happen in scenarios like the following:
+	 * preCAC is running on channel 128 in HT20 mode (note: 124 is already
+	 * marked. Now if the mode is switched to HT40, preCAC is restarted
+	 * and the new channel picked for preCAC is 126 HT40. Here, 124
+	 * will be already marked since it was completed in HT20 mode.
+	 * This may happen for any mode switches (20<->40<->80 MHz).
+	 */
+	if (dfs_is_tree_node_marked_as_cac(curr_node, channel))
+		return;
+
 	while (curr_node) {
-		/* Update the current node's CACed subchannels count only
-		 * if it's less than maximum subchannels, else return.
-		 */
-		if (curr_node->n_caced_subchs <
-		    N_SUBCHS_FOR_BANDWIDTH(curr_node->bandwidth)) {
+		 if (curr_node->n_caced_subchs <
+		     N_SUBCHS_FOR_BANDWIDTH(curr_node->bandwidth))
 			curr_node->n_caced_subchs++;
-		} else {
-			dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,
-				"CAC was done on an already preCACed channel!");
-			return;
-		}
 		curr_node = dfs_descend_precac_tree(curr_node, channel);
 	}
 }