Преглед на файлове

qcacmn: skip MEC ast entry creation in wlan fw

MEC ast entry will be created only in software
and adding MEC entry to wlan fw is skipped.

Change-Id: I6991f9f12ceb22a7c599cdf3cc2a13c996d93671
Nitin Shetty преди 4 години
родител
ревизия
71e3244d46
променени са 7 файла, в които са добавени 528 реда и са изтрити 129 реда
  1. 0 1
      dp/inc/cdp_txrx_cmn_struct.h
  2. 7 1
      dp/wifi3.0/dp_internal.h
  3. 103 1
      dp/wifi3.0/dp_main.c
  4. 260 64
      dp/wifi3.0/dp_peer.c
  5. 95 1
      dp/wifi3.0/dp_peer.h
  6. 24 60
      dp/wifi3.0/dp_rx_err.c
  7. 39 1
      dp/wifi3.0/dp_types.h

+ 0 - 1
dp/inc/cdp_txrx_cmn_struct.h

@@ -402,7 +402,6 @@ enum cdp_txrx_ast_entry_type {
 	CDP_TXRX_AST_TYPE_STATIC, /* static ast entry for connected peer */
 	CDP_TXRX_AST_TYPE_SELF, /* static ast entry for self peer (STA mode) */
 	CDP_TXRX_AST_TYPE_WDS,	/* WDS peer ast entry type*/
-	CDP_TXRX_AST_TYPE_MEC,	/* Multicast echo ast entry type */
 	CDP_TXRX_AST_TYPE_WDS_HM, /* HM WDS entry */
 	CDP_TXRX_AST_TYPE_STA_BSS,	 /* BSS entry(STA mode) */
 	CDP_TXRX_AST_TYPE_DA,	/* AST entry based on Destination address */

+ 7 - 1
dp/wifi3.0/dp_internal.h

@@ -1016,7 +1016,13 @@ static inline void dp_update_vdev_stats(struct dp_soc *soc,
 		DP_STATS_AGGR(_tgtobj, _srcobj, rx.multipass_rx_pkt_drop); \
 	}  while (0)
 
-extern int dp_peer_find_attach(struct dp_soc *soc);
+/**
+ * dp_peer_find_attach() - Allocates memory for peer objects
+ * @soc: SoC handle
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS dp_peer_find_attach(struct dp_soc *soc);
 extern void dp_peer_find_detach(struct dp_soc *soc);
 extern void dp_peer_find_hash_add(struct dp_soc *soc, struct dp_peer *peer);
 extern void dp_peer_find_hash_remove(struct dp_soc *soc, struct dp_peer *peer);

+ 103 - 1
dp/wifi3.0/dp_main.c

@@ -652,6 +652,103 @@ static void dp_service_lmac_rings(void *arg)
 
 #endif
 
+#ifdef FEATURE_MEC
+void dp_peer_mec_flush_entries(struct dp_soc *soc)
+{
+	unsigned int index;
+	struct dp_mec_entry *mecentry, *mecentry_next;
+
+	TAILQ_HEAD(, dp_mec_entry) free_list;
+	TAILQ_INIT(&free_list);
+
+	if (!soc->mec_hash.mask)
+		return;
+
+	if (!soc->mec_hash.bins)
+		return;
+
+	if (!qdf_atomic_read(&soc->mec_cnt))
+		return;
+
+	qdf_spin_lock_bh(&soc->mec_lock);
+	for (index = 0; index <= soc->mec_hash.mask; index++) {
+		if (!TAILQ_EMPTY(&soc->mec_hash.bins[index])) {
+			TAILQ_FOREACH_SAFE(mecentry, &soc->mec_hash.bins[index],
+					   hash_list_elem, mecentry_next) {
+			    dp_peer_mec_detach_entry(soc, mecentry, &free_list);
+			}
+		}
+	}
+	qdf_spin_unlock_bh(&soc->mec_lock);
+
+	dp_peer_mec_free_list(soc, &free_list);
+}
+
+/**
+ * dp_print_mec_entries() - Dump MEC entries in table
+ * @soc: Datapath soc handle
+ *
+ * Return: none
+ */
+static void dp_print_mec_stats(struct dp_soc *soc)
+{
+	int i;
+	uint32_t index;
+	struct dp_mec_entry *mecentry = NULL, *mec_list;
+	uint32_t num_entries = 0;
+
+	DP_PRINT_STATS("MEC Stats:");
+	DP_PRINT_STATS("   Entries Added   = %d", soc->stats.mec.added);
+	DP_PRINT_STATS("   Entries Deleted = %d", soc->stats.mec.deleted);
+
+	if (!qdf_atomic_read(&soc->mec_cnt))
+		return;
+
+	mec_list = qdf_mem_malloc(sizeof(*mecentry) * DP_PEER_MAX_MEC_ENTRY);
+	if (!mec_list) {
+		dp_peer_warn("%pK: failed to allocate mec_list", soc);
+		return;
+	}
+
+	DP_PRINT_STATS("MEC Table:");
+	for (index = 0; index <= soc->mec_hash.mask; index++) {
+		qdf_spin_lock_bh(&soc->mec_lock);
+		if (TAILQ_EMPTY(&soc->mec_hash.bins[index])) {
+			qdf_spin_unlock_bh(&soc->mec_lock);
+			continue;
+		}
+
+		TAILQ_FOREACH(mecentry, &soc->mec_hash.bins[index],
+			      hash_list_elem) {
+			qdf_mem_copy(&mec_list[num_entries], mecentry,
+				     sizeof(*mecentry));
+			num_entries++;
+		}
+		qdf_spin_unlock_bh(&soc->mec_lock);
+	}
+
+	if (!num_entries) {
+		qdf_mem_free(mec_list);
+		return;
+	}
+
+	for (i = 0; i < num_entries; i++) {
+		DP_PRINT_STATS("%6d mac_addr = " QDF_MAC_ADDR_FMT
+			       " is_active = %d pdev_id = %d vdev_id = %d",
+			       i,
+			       QDF_MAC_ADDR_REF(mec_list[i].mac_addr.raw),
+			       mec_list[i].is_active,
+			       mec_list[i].pdev_id,
+			       mec_list[i].vdev_id);
+	}
+	qdf_mem_free(mec_list);
+}
+#else
+static void dp_print_mec_stats(struct dp_soc *soc)
+{
+}
+#endif
+
 static int dp_peer_add_ast_wifi3(struct cdp_soc_t *soc_hdl,
 				 uint8_t vdev_id,
 				 uint8_t *peer_mac,
@@ -862,6 +959,7 @@ static void dp_wds_flush_ast_table_wifi3(struct cdp_soc_t  *soc_hdl)
 			    DP_MOD_ID_CDP);
 
 	qdf_spin_unlock_bh(&soc->ast_lock);
+	dp_peer_mec_flush_entries(soc);
 }
 
 /**
@@ -1275,7 +1373,7 @@ dp_print_peer_ast_entries(struct dp_soc *soc, struct dp_peer *peer, void *arg)
 	struct dp_ast_entry *ase, *tmp_ase;
 	uint32_t num_entries = 0;
 	char type[CDP_TXRX_AST_TYPE_MAX][10] = {
-			"NONE", "STATIC", "SELF", "WDS", "MEC", "HMWDS", "BSS",
+			"NONE", "STATIC", "SELF", "WDS", "HMWDS", "BSS",
 			"DA", "HMWDS_SEC"};
 
 	DP_PEER_ITERATE_ASE_LIST(peer, ase, tmp_ase) {
@@ -4803,6 +4901,8 @@ static void dp_soc_deinit(void *txrx_soc)
 
 	qdf_spinlock_destroy(&soc->ast_lock);
 
+	dp_peer_mec_spinlock_destroy(soc);
+
 	qdf_nbuf_queue_free(&soc->htt_stats.msg);
 
 	qdf_nbuf_queue_free(&soc->invalid_buf_queue);
@@ -8459,6 +8559,7 @@ dp_print_host_stats(struct dp_vdev *vdev,
 		break;
 	case TXRX_AST_STATS:
 		dp_print_ast_stats(pdev->soc);
+		dp_print_mec_stats(pdev->soc);
 		dp_print_peer_table(vdev);
 		break;
 	case TXRX_SRNG_PTR_STATS:
@@ -12526,6 +12627,7 @@ void *dp_soc_init(struct dp_soc *soc, HTC_HANDLE htc_handle,
 	qdf_nbuf_queue_init(&soc->htt_stats.msg);
 
 	qdf_spinlock_create(&soc->ast_lock);
+	dp_peer_mec_spinlock_create(soc);
 
 	qdf_spinlock_create(&soc->reo_desc_freelist_lock);
 	qdf_list_create(&soc->reo_desc_freelist, REO_DESC_FREELIST_SIZE);

+ 260 - 64
dp/wifi3.0/dp_peer.c

@@ -103,7 +103,7 @@ static inline int dp_peer_find_mac_addr_cmp(
 		 & (mac_addr1->align4.bytes_ef == mac_addr2->align4.bytes_ef));
 }
 
-static int dp_peer_ast_table_attach(struct dp_soc *soc)
+static QDF_STATUS dp_peer_ast_table_attach(struct dp_soc *soc)
 {
 	uint32_t max_ast_index;
 
@@ -116,16 +116,16 @@ static int dp_peer_ast_table_attach(struct dp_soc *soc)
 		dp_peer_err("%pK: ast_table memory allocation failed", soc);
 		return QDF_STATUS_E_NOMEM;
 	}
-	return 0; /* success */
+	return QDF_STATUS_SUCCESS; /* success */
 }
 
 /*
  * dp_peer_find_map_attach() - allocate memory for peer_id_to_obj_map
  * @soc: soc handle
  *
- * return: none
+ * return: QDF_STATUS
  */
-static int dp_peer_find_map_attach(struct dp_soc *soc)
+static QDF_STATUS dp_peer_find_map_attach(struct dp_soc *soc)
 {
 	uint32_t max_peers, peer_map_size;
 
@@ -149,7 +149,7 @@ static int dp_peer_find_map_attach(struct dp_soc *soc)
 	qdf_mem_zero(soc->peer_id_to_obj_map, peer_map_size);
 
 	qdf_spinlock_create(&soc->peer_map_lock);
-	return 0; /* success */
+	return QDF_STATUS_SUCCESS; /* success */
 }
 
 static int dp_log2_ceil(unsigned int value)
@@ -176,9 +176,9 @@ static int dp_log2_ceil(unsigned int value)
  * dp_peer_find_hash_attach() - allocate memory for peer_hash table
  * @soc: soc handle
  *
- * return: none
+ * return: QDF_STATUS
  */
-static int dp_peer_find_hash_attach(struct dp_soc *soc)
+static QDF_STATUS dp_peer_find_hash_attach(struct dp_soc *soc)
 {
 	int i, hash_elems, log2;
 
@@ -201,7 +201,7 @@ static int dp_peer_find_hash_attach(struct dp_soc *soc)
 		TAILQ_INIT(&soc->peer_hash.bins[i]);
 
 	qdf_spinlock_create(&soc->peer_hash_lock);
-	return 0;
+	return QDF_STATUS_SUCCESS;
 }
 
 /*
@@ -425,14 +425,228 @@ static bool dp_peer_exist_on_pdev(struct dp_soc *soc,
 	return found;
 }
 
+#ifdef FEATURE_MEC
+/**
+ * dp_peer_mec_hash_attach() - Allocate and initialize MEC Hash Table
+ * @soc: SoC handle
+ *
+ * Return: QDF_STATUS
+ */
+static QDF_STATUS dp_peer_mec_hash_attach(struct dp_soc *soc)
+{
+	int log2, hash_elems, i;
+
+	log2 = dp_log2_ceil(DP_PEER_MAX_MEC_IDX);
+	hash_elems = 1 << log2;
+
+	soc->mec_hash.mask = hash_elems - 1;
+	soc->mec_hash.idx_bits = log2;
+
+	dp_peer_info("%pK: max mec index: %d",
+		     soc, DP_PEER_MAX_MEC_IDX);
+
+	/* allocate an array of TAILQ mec object lists */
+	soc->mec_hash.bins = qdf_mem_malloc(hash_elems *
+					    sizeof(TAILQ_HEAD(anonymous_tail_q,
+							      dp_mec_entry)));
+
+	if (!soc->mec_hash.bins)
+		return QDF_STATUS_E_NOMEM;
+
+	for (i = 0; i < hash_elems; i++)
+		TAILQ_INIT(&soc->mec_hash.bins[i]);
+
+	return QDF_STATUS_SUCCESS;
+}
+
+/**
+ * dp_peer_mec_hash_index() - Compute the MEC hash from MAC address
+ * @soc: SoC handle
+ *
+ * Return: MEC hash
+ */
+static inline uint32_t dp_peer_mec_hash_index(struct dp_soc *soc,
+					      union dp_align_mac_addr *mac_addr)
+{
+	uint32_t index;
+
+	index =
+		mac_addr->align2.bytes_ab ^
+		mac_addr->align2.bytes_cd ^
+		mac_addr->align2.bytes_ef;
+	index ^= index >> soc->mec_hash.idx_bits;
+	index &= soc->mec_hash.mask;
+	return index;
+}
+
+struct dp_mec_entry *dp_peer_mec_hash_find_by_pdevid(struct dp_soc *soc,
+						     uint8_t pdev_id,
+						     uint8_t *mec_mac_addr)
+{
+	union dp_align_mac_addr local_mac_addr_aligned, *mac_addr;
+	uint32_t index;
+	struct dp_mec_entry *mecentry;
+
+	qdf_mem_copy(&local_mac_addr_aligned.raw[0],
+		     mec_mac_addr, QDF_MAC_ADDR_SIZE);
+	mac_addr = &local_mac_addr_aligned;
+
+	index = dp_peer_mec_hash_index(soc, mac_addr);
+	TAILQ_FOREACH(mecentry, &soc->mec_hash.bins[index], hash_list_elem) {
+		if ((pdev_id == mecentry->pdev_id) &&
+		    !dp_peer_find_mac_addr_cmp(mac_addr, &mecentry->mac_addr))
+			return mecentry;
+	}
+
+	return NULL;
+}
+
+/**
+ * dp_peer_mec_hash_add() - Add MEC entry into hash table
+ * @soc: SoC handle
+ *
+ * This function adds the MEC entry into SoC MEC hash table
+ *
+ * Return: None
+ */
+static inline void dp_peer_mec_hash_add(struct dp_soc *soc,
+					struct dp_mec_entry *mecentry)
+{
+	uint32_t index;
+
+	index = dp_peer_mec_hash_index(soc, &mecentry->mac_addr);
+	qdf_spin_lock_bh(&soc->mec_lock);
+	TAILQ_INSERT_TAIL(&soc->mec_hash.bins[index], mecentry, hash_list_elem);
+	qdf_spin_unlock_bh(&soc->mec_lock);
+}
+
+QDF_STATUS dp_peer_mec_add_entry(struct dp_soc *soc,
+				 struct dp_vdev *vdev,
+				 uint8_t *mac_addr)
+{
+	struct dp_mec_entry *mecentry = NULL;
+	struct dp_pdev *pdev = NULL;
+
+	if (!vdev) {
+		dp_peer_err("%pK: Peers vdev is NULL", soc);
+		return QDF_STATUS_E_INVAL;
+	}
+
+	pdev = vdev->pdev;
+
+	if (qdf_unlikely(qdf_atomic_read(&soc->mec_cnt) >=
+					 DP_PEER_MAX_MEC_ENTRY)) {
+		dp_peer_warn("%pK: max MEC entry limit reached mac_addr: "
+			     QDF_MAC_ADDR_FMT, soc, QDF_MAC_ADDR_REF(mac_addr));
+		return QDF_STATUS_E_NOMEM;
+	}
+
+	qdf_spin_lock_bh(&soc->mec_lock);
+	mecentry = dp_peer_mec_hash_find_by_pdevid(soc, pdev->pdev_id,
+						   mac_addr);
+	if (qdf_likely(mecentry)) {
+		mecentry->is_active = TRUE;
+		qdf_spin_unlock_bh(&soc->mec_lock);
+		return QDF_STATUS_E_ALREADY;
+	}
+
+	qdf_spin_unlock_bh(&soc->mec_lock);
+
+	dp_peer_debug("%pK: pdevid: %u vdev: %u type: MEC mac_addr: "
+		      QDF_MAC_ADDR_FMT,
+		      soc, pdev->pdev_id, vdev->vdev_id,
+		      QDF_MAC_ADDR_REF(mac_addr));
+
+	mecentry = (struct dp_mec_entry *)
+			qdf_mem_malloc(sizeof(struct dp_mec_entry));
+
+	if (qdf_unlikely(!mecentry)) {
+		dp_peer_err("%pK: fail to allocate mecentry", soc);
+		return QDF_STATUS_E_NOMEM;
+	}
+
+	qdf_copy_macaddr((struct qdf_mac_addr *)&mecentry->mac_addr.raw[0],
+			 (struct qdf_mac_addr *)mac_addr);
+	mecentry->pdev_id = pdev->pdev_id;
+	mecentry->vdev_id = vdev->vdev_id;
+	mecentry->is_active = TRUE;
+	dp_peer_mec_hash_add(soc, mecentry);
+
+	qdf_atomic_inc(&soc->mec_cnt);
+	DP_STATS_INC(soc, mec.added, 1);
+
+	return QDF_STATUS_SUCCESS;
+}
+
+void dp_peer_mec_detach_entry(struct dp_soc *soc, struct dp_mec_entry *mecentry,
+			      void *ptr)
+{
+	uint32_t index = dp_peer_mec_hash_index(soc, &mecentry->mac_addr);
+
+	TAILQ_HEAD(, dp_mec_entry) * free_list = ptr;
+
+	TAILQ_REMOVE(&soc->mec_hash.bins[index], mecentry,
+		     hash_list_elem);
+	TAILQ_INSERT_TAIL(free_list, mecentry, hash_list_elem);
+}
+
+void dp_peer_mec_free_list(struct dp_soc *soc, void *ptr)
+{
+	struct dp_mec_entry *mecentry, *mecentry_next;
+
+	TAILQ_HEAD(, dp_mec_entry) * free_list = ptr;
+
+	TAILQ_FOREACH_SAFE(mecentry, free_list, hash_list_elem,
+			   mecentry_next) {
+		dp_peer_debug("%pK: MEC delete for mac_addr " QDF_MAC_ADDR_FMT,
+			      soc, QDF_MAC_ADDR_REF(&mecentry->mac_addr));
+		qdf_mem_free(mecentry);
+		qdf_atomic_dec(&soc->mec_cnt);
+		DP_STATS_INC(soc, mec.deleted, 1);
+	}
+}
+
+/**
+ * dp_peer_mec_hash_detach() - Free MEC Hash table
+ * @soc: SoC handle
+ *
+ * Return: None
+ */
+static void dp_peer_mec_hash_detach(struct dp_soc *soc)
+{
+	dp_peer_mec_flush_entries(soc);
+	qdf_mem_free(soc->mec_hash.bins);
+	soc->mec_hash.bins = NULL;
+}
+
+void dp_peer_mec_spinlock_destroy(struct dp_soc *soc)
+{
+	qdf_spinlock_destroy(&soc->mec_lock);
+}
+
+void dp_peer_mec_spinlock_create(struct dp_soc *soc)
+{
+	qdf_spinlock_create(&soc->mec_lock);
+}
+#else
+static QDF_STATUS dp_peer_mec_hash_attach(struct dp_soc *soc)
+{
+	return QDF_STATUS_SUCCESS;
+}
+
+static void dp_peer_mec_hash_detach(struct dp_soc *soc)
+{
+}
+#endif
+
 #ifdef FEATURE_AST
 /*
  * dp_peer_ast_hash_attach() - Allocate and initialize AST Hash Table
  * @soc: SoC handle
  *
- * Return: None
+ * Return: QDF_STATUS
  */
-static int dp_peer_ast_hash_attach(struct dp_soc *soc)
+static QDF_STATUS dp_peer_ast_hash_attach(struct dp_soc *soc)
 {
 	int i, hash_elems, log2;
 	unsigned int max_ast_idx = wlan_cfg_get_max_ast_idx(soc->wlan_cfg_ctx);
@@ -460,7 +674,7 @@ static int dp_peer_ast_hash_attach(struct dp_soc *soc)
 	for (i = 0; i < hash_elems; i++)
 		TAILQ_INIT(&soc->ast_hash.bins[i]);
 
-	return 0;
+	return QDF_STATUS_SUCCESS;
 }
 
 /*
@@ -911,13 +1125,10 @@ QDF_STATUS dp_peer_add_ast(struct dp_soc *soc,
 		ast_entry = dp_peer_ast_hash_find_by_pdevid(soc, mac_addr,
 							    pdev->pdev_id);
 		if (ast_entry) {
-			if ((type == CDP_TXRX_AST_TYPE_MEC) &&
-			    (ast_entry->type == CDP_TXRX_AST_TYPE_MEC))
-				ast_entry->is_active = TRUE;
-
 			qdf_spin_unlock_bh(&soc->ast_lock);
 			return QDF_STATUS_E_ALREADY;
 		}
+
 		if (is_peer_found) {
 			/* During WDS to static roaming, peer is added
 			 * to the list before static AST entry create.
@@ -939,10 +1150,6 @@ QDF_STATUS dp_peer_add_ast(struct dp_soc *soc,
 		ast_entry = dp_peer_ast_hash_find_soc(soc, mac_addr);
 
 		if (ast_entry) {
-			if ((type == CDP_TXRX_AST_TYPE_MEC) &&
-			    (ast_entry->type == CDP_TXRX_AST_TYPE_MEC))
-				ast_entry->is_active = TRUE;
-
 			if ((ast_entry->type == CDP_TXRX_AST_TYPE_WDS_HM) &&
 			    !ast_entry->delete_in_progress) {
 				qdf_spin_unlock_bh(&soc->ast_lock);
@@ -1006,22 +1213,6 @@ QDF_STATUS dp_peer_add_ast(struct dp_soc *soc,
 				return QDF_STATUS_E_AGAIN;
 			}
 
-			/* Modify an already existing AST entry from type
-			 * WDS to MEC on promption. This serves as a fix when
-			 * backbone of interfaces are interchanged wherein
-			 * wds entr becomes its own MEC. The entry should be
-			 * replaced only when the ast_entry peer matches the
-			 * peer received in mec event. This additional check
-			 * is needed in wds repeater cases where a multicast
-			 * packet from station to the root via the repeater
-			 * should not remove the wds entry.
-			 */
-			if ((ast_entry->type == CDP_TXRX_AST_TYPE_WDS) &&
-			    (type == CDP_TXRX_AST_TYPE_MEC) &&
-			    (ast_entry->peer_id == peer->peer_id)) {
-				ast_entry->is_active = FALSE;
-				dp_peer_del_ast(soc, ast_entry);
-			}
 			qdf_spin_unlock_bh(&soc->ast_lock);
 			return QDF_STATUS_E_ALREADY;
 		}
@@ -1072,10 +1263,6 @@ add_ast_entry:
 		TAILQ_INSERT_TAIL(&peer->ast_entry_list, ast_entry,
 				  ase_list_elem);
 		break;
-	case CDP_TXRX_AST_TYPE_MEC:
-		ast_entry->next_hop = 1;
-		ast_entry->type = CDP_TXRX_AST_TYPE_MEC;
-		break;
 	case CDP_TXRX_AST_TYPE_DA:
 		vap_bss_peer = dp_vdev_bss_peer_ref_n_get(soc, vdev,
 							  DP_MOD_ID_AST);
@@ -1097,10 +1284,8 @@ add_ast_entry:
 	soc->num_ast_entries++;
 	dp_peer_ast_hash_add(soc, ast_entry);
 
-	if (type == CDP_TXRX_AST_TYPE_MEC)
-		qdf_mem_copy(next_node_mac, peer->vdev->mac_addr.raw, 6);
-	else
-		qdf_mem_copy(next_node_mac, peer->mac_addr.raw, 6);
+	qdf_copy_macaddr((struct qdf_mac_addr *)next_node_mac,
+			 (struct qdf_mac_addr *)peer->mac_addr.raw);
 
 	if ((ast_entry->type != CDP_TXRX_AST_TYPE_STATIC) &&
 	    (ast_entry->type != CDP_TXRX_AST_TYPE_SELF) &&
@@ -1431,9 +1616,9 @@ struct dp_ast_entry *dp_peer_ast_hash_find_by_pdevid(struct dp_soc *soc,
 	return NULL;
 }
 
-static int dp_peer_ast_hash_attach(struct dp_soc *soc)
+static QDF_STATUS dp_peer_ast_hash_attach(struct dp_soc *soc)
 {
-	return 0;
+	return QDF_STATUS_SUCCESS;
 }
 
 static inline QDF_STATUS dp_peer_map_ast(struct dp_soc *soc,
@@ -1816,31 +2001,41 @@ static void dp_peer_find_map_detach(struct dp_soc *soc)
 	}
 }
 
-int dp_peer_find_attach(struct dp_soc *soc)
+QDF_STATUS dp_peer_find_attach(struct dp_soc *soc)
 {
-	if (dp_peer_find_map_attach(soc))
-		return 1;
+	QDF_STATUS status;
 
-	if (dp_peer_find_hash_attach(soc)) {
-		dp_peer_find_map_detach(soc);
-		return 1;
-	}
+	status = dp_peer_find_map_attach(soc);
+	if (!QDF_IS_STATUS_SUCCESS(status))
+		return status;
 
-	if (dp_peer_ast_table_attach(soc)) {
-		dp_peer_find_hash_detach(soc);
-		dp_peer_find_map_detach(soc);
-		return 1;
-	}
+	status = dp_peer_find_hash_attach(soc);
+	if (!QDF_IS_STATUS_SUCCESS(status))
+		goto map_detach;
 
-	if (dp_peer_ast_hash_attach(soc)) {
-		dp_peer_ast_table_detach(soc);
-		dp_peer_find_hash_detach(soc);
-		dp_peer_find_map_detach(soc);
-		return 1;
+	status = dp_peer_ast_table_attach(soc);
+	if (!QDF_IS_STATUS_SUCCESS(status))
+		goto hash_detach;
+
+	status = dp_peer_ast_hash_attach(soc);
+	if (!QDF_IS_STATUS_SUCCESS(status))
+		goto ast_table_detach;
+
+	status = dp_peer_mec_hash_attach(soc);
+	if (QDF_IS_STATUS_SUCCESS(status)) {
+		dp_soc_wds_attach(soc);
+		return status;
 	}
 
-	dp_soc_wds_attach(soc);
-	return 0; /* success */
+	dp_peer_ast_hash_detach(soc);
+ast_table_detach:
+	dp_peer_ast_table_detach(soc);
+hash_detach:
+	dp_peer_find_hash_detach(soc);
+map_detach:
+	dp_peer_find_map_detach(soc);
+
+	return status;
 }
 
 void dp_rx_tid_stats_cb(struct dp_soc *soc, void *cb_ctxt,
@@ -2180,6 +2375,7 @@ dp_peer_find_detach(struct dp_soc *soc)
 	dp_peer_find_hash_detach(soc);
 	dp_peer_ast_hash_detach(soc);
 	dp_peer_ast_table_detach(soc);
+	dp_peer_mec_hash_detach(soc);
 }
 
 static void dp_rx_tid_update_cb(struct dp_soc *soc, void *cb_ctxt,

+ 95 - 1
dp/wifi3.0/dp_peer.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2021 The Linux Foundation. All rights reserved.
  *
  * Permission to use, copy, modify, and/or distribute this software for
  * any purpose with or without fee is hereby granted, provided that the
@@ -24,6 +24,9 @@
 
 #define DP_INVALID_PEER_ID 0xffff
 
+#define DP_PEER_MAX_MEC_IDX 1024	/* maximum index for MEC table */
+#define DP_PEER_MAX_MEC_ENTRY 4096	/* maximum MEC entries in MEC table */
+
 #define DP_FW_PEER_STATS_CMP_TIMEOUT_MSEC 5000
 
 #define dp_peer_alert(params...) QDF_TRACE_FATAL(QDF_MODULE_ID_DP_PEER, params)
@@ -601,6 +604,59 @@ void dp_peer_unlink_ast_entry(struct dp_soc *soc,
 			      struct dp_ast_entry *ast_entry,
 			      struct dp_peer *peer);
 
+/**
+ * dp_peer_mec_detach_entry() - Detach the MEC entry
+ * @soc: SoC handle
+ * @mecentry: MEC entry of the node
+ * @ptr: pointer to free list
+ *
+ * The MEC entry is detached from MEC table and added to free_list
+ * to free the object outside lock
+ *
+ * Return: None
+ */
+void dp_peer_mec_detach_entry(struct dp_soc *soc, struct dp_mec_entry *mecentry,
+			      void *ptr);
+
+/**
+ * dp_peer_mec_free_list() - free the MEC entry from free_list
+ * @soc: SoC handle
+ * @ptr: pointer to free list
+ *
+ * Return: None
+ */
+void dp_peer_mec_free_list(struct dp_soc *soc, void *ptr);
+
+/**
+ * dp_peer_mec_add_entry()
+ * @soc: SoC handle
+ * @vdev: vdev to which mec node belongs
+ * @mac_addr: MAC address of mec node
+ *
+ * This function allocates and adds MEC entry to MEC table.
+ * It assumes caller has taken the mec lock to protect the access to these
+ * tables
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS dp_peer_mec_add_entry(struct dp_soc *soc,
+				 struct dp_vdev *vdev,
+				 uint8_t *mac_addr);
+
+/**
+ * dp_peer_mec_hash_find_by_pdevid() - Find MEC entry by MAC address
+ * within pdev
+ * @soc: SoC handle
+ *
+ * It assumes caller has taken the mec_lock to protect the access to
+ * MEC hash table
+ *
+ * Return: MEC entry
+ */
+struct dp_mec_entry *dp_peer_mec_hash_find_by_pdevid(struct dp_soc *soc,
+						     uint8_t pdev_id,
+						     uint8_t *mec_mac_addr);
+
 #define DP_AST_ASSERT(_condition) \
 	do { \
 		if (!(_condition)) { \
@@ -850,4 +906,42 @@ static inline void dp_peer_delete_ast_entries(struct dp_soc *soc,
 {
 }
 #endif
+
+#ifdef FEATURE_MEC
+/**
+ * dp_peer_mec_spinlock_create() - Create the MEC spinlock
+ * @soc: SoC handle
+ *
+ * Return: none
+ */
+void dp_peer_mec_spinlock_create(struct dp_soc *soc);
+
+/**
+ * dp_peer_mec_spinlock_destroy() - Destroy the MEC spinlock
+ * @soc: SoC handle
+ *
+ * Return: none
+ */
+void dp_peer_mec_spinlock_destroy(struct dp_soc *soc);
+
+/**
+ * dp_peer_mec_flush_entries() - Delete all mec entries in table
+ * @soc: Datapath SOC
+ *
+ * Return: None
+ */
+void dp_peer_mec_flush_entries(struct dp_soc *soc);
+#else
+static inline void dp_peer_mec_spinlock_create(struct dp_soc *soc)
+{
+}
+
+static inline void dp_peer_mec_spinlock_destroy(struct dp_soc *soc)
+{
+}
+
+static inline void dp_peer_mec_flush_entries(struct dp_soc *soc)
+{
+}
+#endif
 #endif /* _DP_PEER_H_ */

+ 24 - 60
dp/wifi3.0/dp_rx_err.c

@@ -45,6 +45,7 @@
 /* Max buffer in invalid peer SG list*/
 #define DP_MAX_INVALID_BUFFERS 10
 
+#ifdef FEATURE_MEC
 /**
  * dp_rx_mcast_echo_check() - check if the mcast pkt is a loop
  *			      back on same vap or a different vap.
@@ -58,13 +59,13 @@
  *
  */
 static inline bool dp_rx_mcast_echo_check(struct dp_soc *soc,
-					struct dp_peer *peer,
-					uint8_t *rx_tlv_hdr,
-					qdf_nbuf_t nbuf)
+					  struct dp_peer *peer,
+					  uint8_t *rx_tlv_hdr,
+					  qdf_nbuf_t nbuf)
 {
 	struct dp_vdev *vdev = peer->vdev;
-	struct dp_ast_entry *ase = NULL;
-	uint16_t sa_idx = 0;
+	struct dp_pdev *pdev = vdev->pdev;
+	struct dp_mec_entry *mecentry = NULL;
 	uint8_t *data;
 
 	/*
@@ -102,67 +103,30 @@ static inline bool dp_rx_mcast_echo_check(struct dp_soc *soc,
 	 * wireless STAs MAC addr which are behind the Repeater,
 	 * then drop the pkt as it is looped back
 	 */
-	qdf_spin_lock_bh(&soc->ast_lock);
-	if (hal_rx_msdu_end_sa_is_valid_get(soc->hal_soc, rx_tlv_hdr)) {
-		sa_idx = hal_rx_msdu_end_sa_idx_get(soc->hal_soc, rx_tlv_hdr);
-
-		if ((sa_idx < 0) ||
-		    (sa_idx >= wlan_cfg_get_max_ast_idx(soc->wlan_cfg_ctx))) {
-			qdf_spin_unlock_bh(&soc->ast_lock);
-			QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
-					"invalid sa_idx: %d", sa_idx);
-			qdf_assert_always(0);
-		}
+	qdf_spin_lock_bh(&soc->mec_lock);
 
-		ase = soc->ast_table[sa_idx];
-		if (!ase) {
-			/* We do not get a peer map event for STA and without
-			 * this event we don't know what is STA's sa_idx.
-			 * For this reason the AST is still not associated to
-			 * any index postion in ast_table.
-			 * In these kind of scenarios where sa is valid but
-			 * ast is not in ast_table, we use the below API to get
-			 * AST entry for STA's own mac_address.
-			 */
-			ase = dp_peer_ast_hash_find_by_vdevid
-				(soc, &data[QDF_MAC_ADDR_SIZE],
-				 peer->vdev->vdev_id);
-			if (ase) {
-				ase->ast_idx = sa_idx;
-				soc->ast_table[sa_idx] = ase;
-				ase->is_mapped = TRUE;
-			}
-		}
-	} else {
-		ase = dp_peer_ast_hash_find_by_pdevid(soc,
-						      &data[QDF_MAC_ADDR_SIZE],
-						      vdev->pdev->pdev_id);
+	mecentry = dp_peer_mec_hash_find_by_pdevid(soc, pdev->pdev_id,
+						   &data[QDF_MAC_ADDR_SIZE]);
+	if (!mecentry) {
+		qdf_spin_unlock_bh(&soc->mec_lock);
+		return false;
 	}
 
-	if (ase) {
+	qdf_spin_unlock_bh(&soc->mec_lock);
 
-		if (ase->pdev_id != vdev->pdev->pdev_id) {
-			qdf_spin_unlock_bh(&soc->ast_lock);
-			dp_rx_err_info("%pK: Detected DBDC Root AP "QDF_MAC_ADDR_FMT", %d %d",
-				       soc, QDF_MAC_ADDR_REF(&data[QDF_MAC_ADDR_SIZE]),
-				       vdev->pdev->pdev_id,
-				       ase->pdev_id);
-			return false;
-		}
-
-		if ((ase->type == CDP_TXRX_AST_TYPE_MEC) ||
-				(ase->peer_id != peer->peer_id)) {
-			qdf_spin_unlock_bh(&soc->ast_lock);
-			dp_rx_err_info("%pK: received pkt with same src mac "QDF_MAC_ADDR_FMT,
-				       soc, QDF_MAC_ADDR_REF(&data[QDF_MAC_ADDR_SIZE]));
-
-			return true;
-		}
-	}
-	qdf_spin_unlock_bh(&soc->ast_lock);
+	dp_rx_err_info("%pK: received pkt with same src mac " QDF_MAC_ADDR_FMT,
+		       soc, QDF_MAC_ADDR_REF(&data[QDF_MAC_ADDR_SIZE]));
+	return true;
+}
+#else
+static inline bool dp_rx_mcast_echo_check(struct dp_soc *soc,
+					  struct dp_peer *peer,
+					  uint8_t *rx_tlv_hdr,
+					  qdf_nbuf_t nbuf)
+{
 	return false;
 }
-
+#endif
 #endif /* QCA_HOST_MODE_WIFI_DISABLED */
 
 void dp_rx_link_desc_refill_duplicate_check(

+ 39 - 1
dp/wifi3.0/dp_types.h

@@ -851,6 +851,11 @@ struct dp_soc_stats {
 		uint32_t ast_mismatch;
 	} ast;
 
+	struct {
+		uint32_t added;
+		uint32_t deleted;
+	} mec;
+
 	/* SOC level TX stats */
 	struct {
 		/* Total packets transmitted */
@@ -1085,6 +1090,25 @@ struct dp_ast_entry {
 	TAILQ_ENTRY(dp_ast_entry) hash_list_elem;
 };
 
+/*
+ * dp_mec_entry
+ *
+ * @mac_addr:  MAC Address for this MEC entry
+ * @is_active: flag to indicate active data traffic on this node
+ *             (used for aging out/expiry)
+ * @pdev_id: pdev ID
+ * @vdev_id: vdev ID
+ * @hash_list_elem: node in soc MEC hash list (mac address used as hash)
+ */
+struct dp_mec_entry {
+	union dp_align_mac_addr mac_addr;
+	bool is_active;
+	uint8_t pdev_id;
+	uint8_t vdev_id;
+
+	TAILQ_ENTRY(dp_mec_entry) hash_list_elem;
+};
+
 /* SOC level htt stats */
 struct htt_t2h_stats {
 	/* lock to protect htt_stats_msg update */
@@ -1545,7 +1569,6 @@ struct dp_soc {
 		unsigned idx_bits;
 		TAILQ_HEAD(, dp_ast_entry) * bins;
 	} ast_hash;
-
 	struct dp_rx_history *rx_ring_history[MAX_REO_DEST_RINGS];
 	struct dp_rx_err_history *rx_err_ring_history;
 	struct dp_rx_reinject_history *rx_reinject_ring_history;
@@ -1725,6 +1748,21 @@ struct dp_soc {
 #endif
 	/* Invalid buffer that allocated for RX buffer */
 	qdf_nbuf_queue_t invalid_buf_queue;
+
+#ifdef FEATURE_MEC
+	/** @mec_lock: spinlock for MEC table */
+	qdf_spinlock_t mec_lock;
+	/** @mec_cnt: number of active mec entries */
+	qdf_atomic_t mec_cnt;
+	struct {
+		/** @mask: mask bits */
+		uint32_t mask;
+		/** @idx_bits: index to shift bits */
+		uint32_t idx_bits;
+		/** @bins: MEC table */
+		TAILQ_HEAD(, dp_mec_entry) * bins;
+	} mec_hash;
+#endif
 };
 
 #ifdef IPA_OFFLOAD