Browse Source

qcacmn: Add logic to logically delete the scan cache node

When a beacon/probe resp is received the scan cache delete the
old node (N1) and adds a new node(N2) at the end of the list, If any
other process is using old node (N1), for some amount of time driver
will have 2 entry for the same AP. Now if driver again receive a
beacon/probe resp during this time, it end up deleting the older node
(N1) Again and thus making its ref count 0 and freeing it, even when
the process using old node (N1) hasnt freed it.

To fix this add a active state to the scan node, and mark it inactive
or logically delete the node as soon its deleted from ageout, entry update
or flush logic. Also do not use the inactive or logically deleted node
for any new operation. For this add a logic to return only active nodes in
scm_get_next_node.

CRs-Fixed: 2155538
Change-Id: I186d3a0b0540f0b572735e1d95239ddcd8c9bdc9
Abhishek Singh 7 years ago
parent
commit
3f11bd6b0a

+ 80 - 25
umac/scan/core/src/wlan_scan_cache_db.c

@@ -107,11 +107,12 @@ static void scm_scan_entry_get_ref(struct scan_cache_node *scan_node)
  * @scan_db: scan database
  * @scan_node: scan node
  * @lock_needed: if scan_db_lock is needed
+ * @delete: logically delete the entry
  *
  * Return: void
  */
 static void scm_scan_entry_put_ref(struct scan_dbs *scan_db,
-	struct scan_cache_node *scan_node, bool lock_needed)
+	struct scan_cache_node *scan_node, bool lock_needed, bool delete)
 {
 
 	if (!scan_node) {
@@ -120,15 +121,24 @@ static void scm_scan_entry_put_ref(struct scan_dbs *scan_db,
 		return;
 	}
 
+	if (lock_needed)
+		qdf_spin_lock_bh(&scan_db->scan_db_lock);
+
 	if (!qdf_atomic_read(&scan_node->ref_cnt)) {
+		if (lock_needed)
+			qdf_spin_unlock_bh(&scan_db->scan_db_lock);
 		scm_err("scan_node ref cnt is 0");
 		QDF_ASSERT(0);
 		return;
 	}
 
-	if (lock_needed)
-		qdf_spin_lock_bh(&scan_db->scan_db_lock);
-
+	if (delete) {
+		if (!scan_node->active) {
+			scm_err("node is already deleted");
+			QDF_ASSERT(0);
+		}
+		scan_node->active = false;
+	}
 	/* Decrement ref count, free scan_node, if ref count == 0 */
 	if (qdf_atomic_dec_and_test(&scan_node->ref_cnt))
 		scm_del_scan_node_from_db(scan_db, scan_node);
@@ -154,6 +164,7 @@ static void scm_add_scan_node(struct scan_dbs *scan_db,
 
 	qdf_spin_lock_bh(&scan_db->scan_db_lock);
 	qdf_atomic_init(&scan_node->ref_cnt);
+	scan_node->active = true;
 	scm_scan_entry_get_ref(scan_node);
 	qdf_list_insert_back(&scan_db->scan_hash_tbl[hash_idx],
 			&scan_node->node);
@@ -162,11 +173,54 @@ static void scm_add_scan_node(struct scan_dbs *scan_db,
 }
 
 
+/**
+ * scm_get_next_valid_node() - API get the next valid scan node from
+ * the list
+ * @list: hash list
+ * @cur_node: current node pointer
+ *
+ * API to get next active node from the list. If cur_node is NULL
+ * it will return first node of the list.
+ * Call must be protected by scan_db->scan_db_lock
+ *
+ * Return: next scan node
+ */
+static qdf_list_node_t *
+scm_get_next_valid_node(qdf_list_t *list,
+	qdf_list_node_t *cur_node)
+{
+	qdf_list_node_t *next_node = NULL;
+	qdf_list_node_t *temp_node = NULL;
+	struct scan_cache_node *scan_node;
+
+	if (cur_node)
+		qdf_list_peek_next(list, cur_node, &next_node);
+	else
+		qdf_list_peek_front(list, &next_node);
+
+	while (next_node) {
+		scan_node = qdf_container_of(next_node,
+			struct scan_cache_node, node);
+		if (scan_node->active)
+			return next_node;
+		/*
+		 * If node is not valid check for next entry
+		 * to get next valid node.
+		 */
+		qdf_list_peek_next(list, next_node, &temp_node);
+		next_node = temp_node;
+		temp_node = NULL;
+	}
+
+	return next_node;
+}
+
 /**
  * scm_get_next_node() - API get the next scan node from
  * the list
+ * @scan_db: scan data base
  * @list: hash list
- * @scan_node: node to be removed
+ * @cur_node: current node pointer
  *
  * API get the next node from the list. If cur_node is NULL
  * it will return first node of the list
@@ -175,21 +229,19 @@ static void scm_add_scan_node(struct scan_dbs *scan_db,
  */
 static struct scan_cache_node *
 scm_get_next_node(struct scan_dbs *scan_db,
-	qdf_list_t *list,
-	struct scan_cache_node *cur_node)
+	qdf_list_t *list, struct scan_cache_node *cur_node)
 {
 	struct scan_cache_node *next_node = NULL;
 	qdf_list_node_t *next_list = NULL;
 
 	qdf_spin_lock_bh(&scan_db->scan_db_lock);
 	if (cur_node) {
-		qdf_list_peek_next(list,
-			&cur_node->node, &next_list);
+		next_list = scm_get_next_valid_node(list, &cur_node->node);
 		/* Decrement the ref count of the previous node */
 		scm_scan_entry_put_ref(scan_db,
-			cur_node, false);
+			cur_node, false, false);
 	} else {
-		qdf_list_peek_front(list, &next_list);
+		next_list = scm_get_next_valid_node(list, NULL);
 	}
 	/* Increase the ref count of the obtained node */
 	if (next_list) {
@@ -219,7 +271,7 @@ static void scm_check_and_age_out(struct scan_dbs *scan_db,
 		scm_info("Aging out BSSID: %pM with age %d ms",
 			node->entry->bssid.bytes,
 			util_scan_entry_age(node->entry));
-		scm_scan_entry_put_ref(scan_db, node, true);
+		scm_scan_entry_put_ref(scan_db, node, true, true);
 	}
 }
 
@@ -258,16 +310,16 @@ static QDF_STATUS scm_flush_oldest_entry(struct scan_dbs *scan_db)
 	int i;
 	struct scan_cache_node *oldest_node = NULL;
 	struct scan_cache_node *cur_node;
-	qdf_list_node_t *cur_list = NULL;
+	qdf_list_node_t *cur_list;
 
 	qdf_spin_lock_bh(&scan_db->scan_db_lock);
 	for (i = 0 ; i < SCAN_HASH_SIZE; i++) {
-		cur_node = NULL;
-		qdf_list_peek_front(&scan_db->scan_hash_tbl[i],
-				&cur_list);
+		/* Get the first valid node for the hash */
+		cur_list = scm_get_next_valid_node(&scan_db->scan_hash_tbl[i],
+						   NULL);
 		/*
-		 * Check only the first node if present as new
-		 * entry are added to tail and thus first
+		 * Check only the first valid node if present as new
+		 * entry are added to tail and thus first valid
 		 * node is the oldest
 		 */
 		if (cur_list) {
@@ -279,7 +331,10 @@ static QDF_STATUS scm_flush_oldest_entry(struct scan_dbs *scan_db)
 				oldest_node = cur_node;
 		}
 	}
-	scm_scan_entry_put_ref(scan_db, oldest_node, false);
+	scm_debug("Flush oldest BSSID: %pM with age %d ms",
+			oldest_node->entry->bssid.bytes,
+			util_scan_entry_age(oldest_node->entry));
+	scm_scan_entry_put_ref(scan_db, oldest_node, false, true);
 	qdf_spin_unlock_bh(&scan_db->scan_db_lock);
 
 	return QDF_STATUS_SUCCESS;
@@ -452,7 +507,7 @@ static void scm_delete_duplicate_entry(struct scan_dbs *scan_db,
 	scm_update_mlme_info(scan_entry, scan_params);
 
 	/* Mark delete the duplicate node */
-	scm_scan_entry_put_ref(scan_db, scan_node, true);
+	scm_scan_entry_put_ref(scan_db, scan_node, true, true);
 }
 
 /**
@@ -482,7 +537,7 @@ scm_find_duplicate_and_del(struct scan_dbs *scan_db,
 			scm_delete_duplicate_entry(scan_db,
 				entry, cur_node);
 			scm_scan_entry_put_ref(scan_db,
-				cur_node, true);
+				cur_node, true, false);
 			return true;
 		}
 		next_node = scm_get_next_node(scan_db,
@@ -881,7 +936,7 @@ scm_iterate_db_and_call_func(struct scan_dbs *scan_db,
 			status = func(arg, cur_node->entry);
 			if (QDF_IS_STATUS_ERROR(status)) {
 				scm_scan_entry_put_ref(scan_db,
-					cur_node, true);
+					cur_node, true, false);
 				return status;
 			}
 			next_node = scm_get_next_node(scan_db,
@@ -956,7 +1011,7 @@ scm_scan_apply_filter_flush_entry(struct wlan_objmgr_psoc *psoc,
 	if (!match)
 		return QDF_STATUS_SUCCESS;
 
-	scm_scan_entry_put_ref(scan_db, db_node, true);
+	scm_scan_entry_put_ref(scan_db, db_node, true, true);
 
 	return QDF_STATUS_SUCCESS;
 }
@@ -1044,7 +1099,7 @@ static void scm_filter_channels(struct scan_dbs *scan_db,
 	}
 
 	if (!match)
-		scm_scan_entry_put_ref(scan_db, db_node, true);
+		scm_scan_entry_put_ref(scan_db, db_node, true, true);
 
 }
 
@@ -1200,7 +1255,7 @@ QDF_STATUS scm_update_scan_mlme_info(struct wlan_objmgr_pdev *pdev,
 			scm_update_mlme_info(entry, cur_node->entry);
 			qdf_spin_unlock_bh(&scan_db->scan_db_lock);
 			scm_scan_entry_put_ref(scan_db,
-					cur_node, true);
+					cur_node, true, false);
 			return QDF_STATUS_SUCCESS;
 		}
 		next_node = scm_get_next_node(scan_db,

+ 2 - 0
umac/scan/dispatcher/inc/wlan_scan_public_structs.h

@@ -228,11 +228,13 @@ struct bss_info {
  * struct scan_cache_node - Scan cache entry node
  * @node: node pointers
  * @ref_cnt: ref count if in use
+ * @active: if entry is logically active
  * @entry: scan entry pointer
  */
 struct scan_cache_node {
 	qdf_list_node_t node;
 	qdf_atomic_t ref_cnt;
+	bool active;
 	struct scan_cache_entry *entry;
 };