Browse Source

qcacmn: Add support for WDS aging

Add support for monitoring receive traffic from WDS nodes and
aging out inactive WDS nodes

Change-Id: Iedea826832a5421714c00bccadc6e0dc4acd4c7e
CRs-Fixed: 2004658
Pamidipati, Vijay 7 years ago
parent
commit
b8bbf16192
7 changed files with 614 additions and 113 deletions
  1. 1 0
      dp/inc/cdp_txrx_cmn_struct.h
  2. 154 18
      dp/wifi3.0/dp_main.c
  3. 312 54
      dp/wifi3.0/dp_peer.c
  4. 17 0
      dp/wifi3.0/dp_peer.h
  5. 45 24
      dp/wifi3.0/dp_rx.h
  6. 60 17
      dp/wifi3.0/dp_types.h
  7. 25 0
      dp/wifi3.0/hal_rx.h

+ 1 - 0
dp/inc/cdp_txrx_cmn_struct.h

@@ -485,6 +485,7 @@ enum cdp_vdev_param_type {
 	CDP_ENABLE_WDS,
 	CDP_ENABLE_PROXYSTA,
 	CDP_UPDATE_TDLS_FLAGS,
+	CDP_CFG_WDS_AGING_TIMER,
 };
 
 #define TXRX_FW_STATS_TXSTATS                     1

+ 154 - 18
dp/wifi3.0/dp_main.c

@@ -40,6 +40,7 @@
 #include "qdf_mem.h"   /* qdf_mem_malloc,free */
 
 #define DP_INTR_POLL_TIMER_MS	10
+#define DP_WDS_AGING_TIMER_DEFAULT_MS	6000
 #define DP_MCS_LENGTH (6*MAX_MCS)
 #define DP_NSS_LENGTH (6*SS_COUNT)
 #define DP_RXDMA_ERR_LENGTH (6*MAX_RXDMA_ERRORS)
@@ -877,6 +878,97 @@ static void dp_hw_link_desc_pool_cleanup(struct dp_soc *soc)
 #define RXDMA_MONITOR_DESC_RING_SIZE 1024
 #define RXDMA_ERR_DST_RING_SIZE 1024
 
+/*
+ * dp_wds_aging_timer_fn() - Timer callback function for WDS aging
+ * @soc: Datapath SOC handle
+ *
+ * This is a timer function used to age out stale WDS nodes from
+ * AST table
+ */
+#ifdef FEATURE_WDS
+static void dp_wds_aging_timer_fn(void *soc_hdl)
+{
+	struct dp_soc *soc = (struct dp_soc *) soc_hdl;
+	struct dp_pdev *pdev;
+	struct dp_vdev *vdev;
+	struct dp_peer *peer;
+	struct dp_ast_entry *ase;
+	int i;
+
+	qdf_spin_lock_bh(&soc->ast_lock);
+
+	for (i = 0; i < MAX_PDEV_CNT && soc->pdev_list[i]; i++) {
+		pdev = soc->pdev_list[i];
+		DP_PDEV_ITERATE_VDEV_LIST(pdev, vdev) {
+			DP_VDEV_ITERATE_PEER_LIST(vdev, peer) {
+				DP_PEER_ITERATE_ASE_LIST(peer, ase) {
+					/*
+					 * Do not expire static ast entries
+					 */
+					if (ase->is_static)
+						continue;
+
+					if (ase->is_active) {
+						ase->is_active = FALSE;
+						continue;
+					}
+
+					soc->cdp_soc.ol_ops->peer_del_wds_entry(
+							pdev->osif_pdev,
+							ase->mac_addr.raw);
+
+					dp_peer_del_ast(soc, ase);
+				}
+			}
+		}
+
+	}
+
+	qdf_spin_unlock_bh(&soc->ast_lock);
+
+	if (qdf_atomic_read(&soc->cmn_init_done))
+		qdf_timer_mod(&soc->wds_aging_timer, DP_WDS_AGING_TIMER_DEFAULT_MS);
+}
+
+/*
+ * dp_soc_wds_attach() - Setup WDS timer and AST table
+ * @soc:		Datapath SOC handle
+ *
+ * Return: None
+ */
+static void dp_soc_wds_attach(struct dp_soc *soc)
+{
+	qdf_spinlock_create(&soc->ast_lock);
+
+	qdf_timer_init(soc->osdev, &soc->wds_aging_timer,
+			dp_wds_aging_timer_fn, (void *)soc,
+			QDF_TIMER_TYPE_WAKE_APPS);
+
+	qdf_timer_mod(&soc->wds_aging_timer, DP_WDS_AGING_TIMER_DEFAULT_MS);
+}
+
+/*
+ * dp_soc_wds_detach() - Detach WDS data structures and timers
+ * @txrx_soc: DP SOC handle
+ *
+ * Return: None
+ */
+static void dp_soc_wds_detach(struct dp_soc *soc)
+{
+	qdf_timer_stop(&soc->wds_aging_timer);
+	qdf_timer_free(&soc->wds_aging_timer);
+	qdf_spinlock_destroy(&soc->ast_lock);
+}
+#else
+static void dp_soc_wds_attach(struct dp_soc *soc)
+{
+}
+
+static void dp_soc_wds_detach(struct dp_soc *soc)
+{
+}
+#endif
+
 /*
  * dp_soc_cmn_setup() - Common SoC level initializion
  * @soc:		Datapath SOC handle
@@ -1035,6 +1127,8 @@ static int dp_soc_cmn_setup(struct dp_soc *soc)
 		goto fail1;
 	}
 
+	dp_soc_wds_attach(soc);
+
 	/* Setup HW REO */
 	qdf_mem_zero(&reo_params, sizeof(reo_params));
 
@@ -1578,6 +1672,9 @@ static void dp_soc_detach_wifi3(void *txrx_soc)
 
 	dp_reo_desc_freelist_destroy(soc);
 	wlan_cfg_soc_detach(soc->wlan_cfg_ctx);
+
+	dp_soc_wds_detach(soc);
+
 	qdf_mem_free(soc);
 }
 
@@ -1960,11 +2057,8 @@ static void *dp_peer_create_wifi3(struct cdp_vdev *vdev_handle,
 	qdf_mem_zero(peer, sizeof(struct dp_peer));
 
 	TAILQ_INIT(&peer->ast_entry_list);
-	qdf_mem_copy(&peer->self_ast_entry.mac_addr, peer_mac_addr,
-			DP_MAC_ADDR_LEN);
-	peer->self_ast_entry.peer = peer;
-	TAILQ_INSERT_TAIL(&peer->ast_entry_list, &peer->self_ast_entry,
-				ast_entry_elem);
+
+	dp_peer_add_ast(soc, peer, peer_mac_addr, 1);
 
 	qdf_spinlock_create(&peer->peer_info_lock);
 
@@ -2008,6 +2102,7 @@ static void *dp_peer_create_wifi3(struct cdp_vdev *vdev_handle,
 		vdev->vap_bss_peer = peer;
 	}
 
+
 #ifndef CONFIG_WIN
 	dp_local_peer_id_alloc(pdev, peer);
 #endif
@@ -2257,8 +2352,6 @@ void dp_peer_unref_delete(void *peer_handle)
 	struct dp_peer *tmppeer;
 	int found = 0;
 	uint16_t peer_id;
-	uint16_t hw_peer_id;
-	struct dp_ast_entry *ast_entry;
 
 	/*
 	 * Hold the lock all the way from checking if the peer ref count
@@ -2342,17 +2435,6 @@ void dp_peer_unref_delete(void *peer_handle)
 #ifdef notyet
 		qdf_mempool_free(soc->osdev, soc->mempool_ol_ath_peer, peer);
 #else
-		TAILQ_FOREACH(ast_entry, &peer->ast_entry_list,
-				ast_entry_elem) {
-			hw_peer_id = ast_entry->ast_idx;
-			if (peer->self_ast_entry.ast_idx != hw_peer_id)
-				qdf_mem_free(ast_entry);
-			else
-				peer->self_ast_entry.ast_idx =
-							HTT_INVALID_PEER;
-
-			soc->ast_table[hw_peer_id] = NULL;
-		}
 		qdf_mem_free(peer);
 #endif
 		if (soc->cdp_soc.ol_ops->peer_unref_delete) {
@@ -3584,6 +3666,14 @@ static void dp_set_vdev_param(struct cdp_vdev *vdev_handle,
 	case CDP_UPDATE_TDLS_FLAGS:
 		vdev->tdls_link_connected = val;
 		break;
+	case CDP_CFG_WDS_AGING_TIMER:
+		if (val == 0)
+			qdf_timer_stop(&vdev->pdev->soc->wds_aging_timer);
+		else if (val != vdev->wds_aging_timer_val)
+			qdf_timer_mod(&vdev->pdev->soc->wds_aging_timer, val);
+
+		vdev->wds_aging_timer_val = val;
+		break;
 	default:
 		break;
 	}
@@ -3913,6 +4003,48 @@ static struct cdp_wds_ops dp_ops_wds = {
 	.vdev_set_wds = dp_vdev_set_wds,
 };
 
+/*
+ * dp_peer_delete_ast_entries(): Delete all AST entries for a peer
+ * @soc - datapath soc handle
+ * @peer - datapath peer handle
+ *
+ * Delete the AST entries belonging to a peer
+ */
+#ifdef FEATURE_WDS
+static inline void dp_peer_delete_ast_entries(struct dp_soc *soc,
+		struct dp_peer *peer)
+{
+	struct dp_ast_entry *ast_entry;
+	qdf_spin_lock_bh(&soc->ast_lock);
+	DP_PEER_ITERATE_ASE_LIST(peer, ast_entry) {
+		if (ast_entry->next_hop) {
+			soc->cdp_soc.ol_ops->peer_del_wds_entry(
+					soc->osif_soc,
+					ast_entry->mac_addr.raw);
+		}
+
+		dp_peer_del_ast(soc, ast_entry);
+	}
+	qdf_spin_unlock_bh(&soc->ast_lock);
+}
+#else
+static inline void dp_peer_delete_ast_entries(struct dp_soc *soc,
+		struct dp_peer *peer)
+{
+}
+#endif
+
+#ifdef CONFIG_WIN
+static void dp_peer_teardown_wifi3(struct cdp_vdev *vdev_hdl, void *peer_hdl)
+{
+	struct dp_vdev *vdev = (struct dp_vdev *) vdev_hdl;
+	struct dp_peer *peer = (struct dp_peer *) peer_hdl;
+	struct dp_soc *soc = (struct dp_soc *) vdev->pdev->soc;
+
+	dp_peer_delete_ast_entries(soc, peer);
+}
+#endif
+
 static struct cdp_cmn_ops dp_ops_cmn = {
 	.txrx_soc_attach_target = dp_soc_attach_target_wifi3,
 	.txrx_vdev_attach = dp_vdev_attach_wifi3,
@@ -3921,7 +4053,11 @@ static struct cdp_cmn_ops dp_ops_cmn = {
 	.txrx_pdev_detach = dp_pdev_detach_wifi3,
 	.txrx_peer_create = dp_peer_create_wifi3,
 	.txrx_peer_setup = dp_peer_setup_wifi3,
+#ifdef CONFIG_WIN
+	.txrx_peer_teardown = dp_peer_teardown_wifi3,
+#else
 	.txrx_peer_teardown = NULL,
+#endif
 	.txrx_peer_delete = dp_peer_delete_wifi3,
 	.txrx_vdev_register = dp_vdev_register_wifi3,
 	.txrx_soc_detach = dp_soc_detach_wifi3,

+ 312 - 54
dp/wifi3.0/dp_peer.c

@@ -124,6 +124,9 @@ static int dp_peer_find_add_id_to_obj(
 #define DP_PEER_HASH_LOAD_MULT  2
 #define DP_PEER_HASH_LOAD_SHIFT 0
 
+#define DP_AST_HASH_LOAD_MULT  2
+#define DP_AST_HASH_LOAD_SHIFT 0
+
 static int dp_peer_find_hash_attach(struct dp_soc *soc)
 {
 	int i, hash_elems, log2;
@@ -186,6 +189,300 @@ void dp_peer_find_hash_add(struct dp_soc *soc, struct dp_peer *peer)
 	qdf_spin_unlock_bh(&soc->peer_ref_mutex);
 }
 
+#ifdef FEATURE_WDS
+/*
+ * dp_peer_ast_hash_attach() - Allocate and initialize AST Hash Table
+ * @soc: SoC handle
+ *
+ * Return: None
+ */
+static int dp_peer_ast_hash_attach(struct dp_soc *soc)
+{
+	int i, hash_elems, log2;
+
+	hash_elems = ((WLAN_UMAC_PSOC_MAX_PEERS * DP_AST_HASH_LOAD_MULT) >>
+		DP_AST_HASH_LOAD_SHIFT);
+
+	log2 = dp_log2_ceil(hash_elems);
+	hash_elems = 1 << log2;
+
+	soc->ast_hash.mask = hash_elems - 1;
+	soc->ast_hash.idx_bits = log2;
+
+	/* allocate an array of TAILQ peer object lists */
+	soc->ast_hash.bins = qdf_mem_malloc(
+		hash_elems * sizeof(TAILQ_HEAD(anonymous_tail_q,
+				dp_ast_entry)));
+
+	if (!soc->ast_hash.bins)
+		return QDF_STATUS_E_NOMEM;
+
+	for (i = 0; i < hash_elems; i++)
+		TAILQ_INIT(&soc->ast_hash.bins[i]);
+
+	return 0;
+}
+
+/*
+ * dp_peer_ast_hash_detach() - Free AST Hash table
+ * @soc: SoC handle
+ *
+ * Return: None
+ */
+static void dp_peer_ast_hash_detach(struct dp_soc *soc)
+{
+	qdf_mem_free(soc->ast_hash.bins);
+}
+
+/*
+ * dp_peer_ast_hash_index() - Compute the AST hash from MAC address
+ * @soc: SoC handle
+ *
+ * Return: AST hash
+ */
+static inline uint32_t dp_peer_ast_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->ast_hash.idx_bits;
+	index &= soc->ast_hash.mask;
+	return index;
+}
+
+/*
+ * dp_peer_ast_hash_add() - Add AST entry into hash table
+ * @soc: SoC handle
+ *
+ * This function adds the AST entry into SoC AST hash table
+ * It assumes caller has taken the ast lock to protect the access to this table
+ *
+ * Return: None
+ */
+static inline void dp_peer_ast_hash_add(struct dp_soc *soc,
+		struct dp_ast_entry *ase)
+{
+	uint32_t index;
+
+	index = dp_peer_ast_hash_index(soc, &ase->mac_addr);
+	TAILQ_INSERT_TAIL(&soc->ast_hash.bins[index], ase, hash_list_elem);
+}
+
+/*
+ * dp_peer_ast_hash_remove() - Look up and remove AST entry from hash table
+ * @soc: SoC handle
+ *
+ * This function removes the AST entry from soc AST hash table
+ * It assumes caller has taken the ast lock to protect the access to this table
+ *
+ * Return: None
+ */
+static inline void dp_peer_ast_hash_remove(struct dp_soc *soc,
+		struct dp_ast_entry *ase)
+{
+	unsigned index;
+	struct dp_ast_entry *tmpase;
+	int found = 0;
+
+	index = dp_peer_ast_hash_index(soc, &ase->mac_addr);
+	/* Check if tail is not empty before delete*/
+	QDF_ASSERT(!TAILQ_EMPTY(&soc->ast_hash.bins[index]));
+
+	TAILQ_FOREACH(tmpase, &soc->ast_hash.bins[index], hash_list_elem) {
+		if (tmpase == ase) {
+			found = 1;
+			break;
+		}
+	}
+
+	QDF_ASSERT(found);
+	TAILQ_REMOVE(&soc->ast_hash.bins[index], ase, hash_list_elem);
+}
+
+/*
+ * dp_peer_ast_hash_find() - Find AST entry by MAC address
+ * @soc: SoC handle
+ *
+ * It assumes caller has taken the ast lock to protect the access to
+ * AST hash table
+ *
+ * Return: AST entry
+ */
+struct dp_ast_entry *dp_peer_ast_hash_find(struct dp_soc *soc,
+	uint8_t *ast_mac_addr, int mac_addr_is_aligned)
+{
+	union dp_align_mac_addr local_mac_addr_aligned, *mac_addr;
+	unsigned index;
+	struct dp_ast_entry *ase;
+
+	if (mac_addr_is_aligned) {
+		mac_addr = (union dp_align_mac_addr *) ast_mac_addr;
+	} else {
+		qdf_mem_copy(
+			&local_mac_addr_aligned.raw[0],
+			ast_mac_addr, DP_MAC_ADDR_LEN);
+		mac_addr = &local_mac_addr_aligned;
+	}
+
+	index = dp_peer_ast_hash_index(soc, mac_addr);
+	TAILQ_FOREACH(ase, &soc->ast_hash.bins[index], hash_list_elem) {
+		if (dp_peer_find_mac_addr_cmp(mac_addr, &ase->mac_addr) == 0) {
+			return ase;
+		}
+	}
+
+	return NULL;
+}
+
+/*
+ * dp_peer_map_ast() - Map the ast entry with HW AST Index
+ * @soc: SoC handle
+ * @peer: peer to which ast node belongs
+ * @mac_addr: MAC address of ast node
+ * @hw_peer_id: HW AST Index returned by target in peer map event
+ * @vdev_id: vdev id for VAP to which the peer belongs to
+ *
+ * Return: None
+ */
+static inline void dp_peer_map_ast(struct dp_soc *soc,
+	struct dp_peer *peer, uint8_t *mac_addr, uint16_t hw_peer_id,
+	uint8_t vdev_id)
+{
+	struct dp_ast_entry *ast_entry;
+
+	if (!peer) {
+		return;
+	}
+
+	QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
+		"%s: peer %p ID %d vid %d mac %02x:%02x:%02x:%02x:%02x:%02x\n",
+		__func__, peer, hw_peer_id, vdev_id, mac_addr[0],
+		mac_addr[1], mac_addr[2], mac_addr[3],
+		mac_addr[4], mac_addr[5]);
+
+	qdf_spin_lock_bh(&soc->ast_lock);
+	TAILQ_FOREACH(ast_entry, &peer->ast_entry_list, ase_list_elem) {
+		if (!(qdf_mem_cmp(mac_addr, ast_entry->mac_addr.raw,
+				DP_MAC_ADDR_LEN))) {
+			qdf_spin_unlock_bh(&soc->ast_lock);
+			ast_entry->ast_idx = hw_peer_id;
+			soc->ast_table[hw_peer_id] = ast_entry;
+			ast_entry->is_active = TRUE;
+			return;
+		}
+	}
+	qdf_spin_unlock_bh(&soc->ast_lock);
+
+	if (soc->cdp_soc.ol_ops->peer_map_event) {
+		soc->cdp_soc.ol_ops->peer_map_event(soc->osif_soc,
+				peer->peer_ids[0], hw_peer_id, vdev_id,
+				mac_addr);
+	}
+
+	QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
+			"AST entry not found\n");
+	return;
+}
+
+/*
+ * dp_peer_add_ast() - Allocate and add AST entry into peer list
+ * @soc: SoC handle
+ * @peer: peer to which ast node belongs
+ * @mac_addr: MAC address of ast node
+ * @is_self: Is this base AST entry with peer mac address
+ *
+ * This API is used by WDS source port learning funtion to
+ * add a new AST entry into peer AST list
+ *
+ * Return: 0 if new entry is allocated,
+ *         1 if entry already exists or if allocation has failed
+ */
+int dp_peer_add_ast(struct dp_soc *soc, struct dp_peer *peer,
+		uint8_t *mac_addr, bool is_self)
+{
+	struct dp_ast_entry *ast_entry;
+
+	QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
+		"%s: peer %p mac %02x:%02x:%02x:%02x:%02x:%02x\n",
+		__func__, peer, mac_addr[0], mac_addr[1], mac_addr[2],
+		mac_addr[3], mac_addr[4], mac_addr[5]);
+
+	qdf_spin_lock_bh(&soc->ast_lock);
+
+	/* If AST entry already exists , just return from here */
+	if (dp_peer_ast_hash_find(soc, mac_addr, 0)) {
+		qdf_spin_unlock_bh(&soc->ast_lock);
+		return 1;
+	}
+
+	ast_entry = (struct dp_ast_entry *)
+			qdf_mem_malloc(sizeof(struct dp_ast_entry));
+
+	if (!ast_entry) {
+		qdf_spin_unlock_bh(&soc->ast_lock);
+		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
+			FL("fail to allocate ast_entry"));
+		QDF_ASSERT(0);
+		return 1;
+	}
+
+	qdf_mem_copy(&ast_entry->mac_addr.raw[0], mac_addr, DP_MAC_ADDR_LEN);
+	ast_entry->peer = peer;
+
+	if (is_self) {
+		peer->self_ast_entry = ast_entry;
+		ast_entry->is_static = TRUE;
+	} else {
+		ast_entry->next_hop = 1;
+		ast_entry->is_static = FALSE;
+	}
+
+	ast_entry->is_active = TRUE;
+	TAILQ_INSERT_TAIL(&peer->ast_entry_list, ast_entry, ase_list_elem);
+	dp_peer_ast_hash_add(soc, ast_entry);
+	qdf_spin_unlock_bh(&soc->ast_lock);
+	return 0;
+}
+
+/*
+ * dp_peer_del_ast() - Delete and free AST entry
+ * @soc: SoC handle
+ * @ast_entry: AST entry of the node
+ *
+ * This function removes the AST entry from peer and soc tables
+ * It assumes caller has taken the ast lock to protect the access to these
+ * tables
+ *
+ * Return: None
+ */
+void dp_peer_del_ast(struct dp_soc *soc,
+		struct dp_ast_entry *ast_entry)
+{
+	struct dp_peer *peer = ast_entry->peer;
+	soc->ast_table[ast_entry->ast_idx] = NULL;
+	TAILQ_REMOVE(&peer->ast_entry_list, ast_entry, ase_list_elem);
+	dp_peer_ast_hash_remove(soc, ast_entry);
+	qdf_mem_free(ast_entry);
+}
+#else
+static int dp_peer_ast_hash_attach(struct dp_soc *soc)
+{
+	return 0;
+}
+static void dp_peer_ast_hash_detach(struct dp_soc *soc)
+{
+}
+static inline void dp_peer_map_ast(struct dp_soc *soc, struct dp_peer *peer,
+		uint8_t *mac_addr, uint16_t hw_peer_id, uint8_t vdev_id)
+{
+}
+
+#endif
+
 #if ATH_SUPPORT_WRAP
 static struct dp_peer *dp_peer_find_hash_find(struct dp_soc *soc,
 	uint8_t *peer_mac_addr, int mac_addr_is_aligned, uint8_t vdev_id)
@@ -315,6 +612,12 @@ int dp_peer_find_attach(struct dp_soc *soc)
 		dp_peer_find_map_detach(soc);
 		return 1;
 	}
+
+	if (dp_peer_ast_hash_attach(soc)) {
+		dp_peer_find_hash_detach(soc);
+		dp_peer_find_map_detach(soc);
+		return 1;
+	}
 	return 0; /* success */
 }
 
@@ -383,7 +686,7 @@ static void dp_rx_tid_stats_cb(struct dp_soc *soc, void *cb_ctxt,
 		queue_status->hole_cnt);
 }
 
-static inline void dp_peer_find_add_id(struct dp_soc *soc,
+static inline struct dp_peer *dp_peer_find_add_id(struct dp_soc *soc,
 	uint8_t *peer_mac_addr, uint16_t peer_id, uint16_t hw_peer_id,
 	uint8_t vdev_id)
 {
@@ -411,55 +714,16 @@ static inline void dp_peer_find_add_id(struct dp_soc *soc,
 			  "%s: ref_cnt: %d", __func__,
 			   qdf_atomic_read(&peer->ref_cnt));
 		soc->peer_id_to_obj_map[peer_id] = peer;
-		peer->self_ast_entry.ast_idx = hw_peer_id;
-		soc->ast_table[hw_peer_id] = &peer->self_ast_entry;
 
 		if (dp_peer_find_add_id_to_obj(peer, peer_id)) {
 			/* TBDXXX: assert for now */
 			QDF_ASSERT(0);
 		}
 
-		return;
+		return peer;
 	}
-}
-
-static inline void dp_peer_add_ast(struct dp_soc *soc,
-	struct dp_peer *peer, uint8_t *peer_mac_addr, uint16_t hw_peer_id,
-	uint8_t vdev_id)
-{
-	struct dp_ast_entry *ast_entry;
 
-	QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
-		"%s: peer %p ID %d vid %d mac %02x:%02x:%02x:%02x:%02x:%02x\n",
-		__func__, peer, hw_peer_id, vdev_id, peer_mac_addr[0],
-		peer_mac_addr[1], peer_mac_addr[2], peer_mac_addr[3],
-		peer_mac_addr[4], peer_mac_addr[5]);
-
-	TAILQ_FOREACH(ast_entry, &peer->ast_entry_list, ast_entry_elem) {
-		if (!(qdf_mem_cmp(peer_mac_addr, ast_entry->mac_addr,
-				DP_MAC_ADDR_LEN))) {
-			soc->ast_table[ast_entry->ast_idx] = NULL;
-			ast_entry->ast_idx = hw_peer_id;
-			soc->ast_table[hw_peer_id] = ast_entry;
-			return;
-		}
-	}
-
-	ast_entry = (struct dp_ast_entry *)
-			qdf_mem_malloc(sizeof(struct dp_ast_entry));
-
-	if (!ast_entry) {
-		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
-			FL("fail to allocate ast_entry for: %d"), hw_peer_id);
-		QDF_ASSERT(0);
-	}
-
-	qdf_mem_copy(&ast_entry->mac_addr, peer_mac_addr, DP_MAC_ADDR_LEN);
-	ast_entry->peer = peer;
-	ast_entry->next_hop = 1;
-	TAILQ_INSERT_TAIL(&peer->ast_entry_list, ast_entry, ast_entry_elem);
-	soc->ast_table[hw_peer_id] = ast_entry;
-	return;
+	return NULL;
 }
 
 /**
@@ -483,7 +747,6 @@ dp_rx_peer_map_handler(void *soc_handle, uint16_t peer_id, uint16_t hw_peer_id,
 	struct dp_soc *soc = (struct dp_soc *)soc_handle;
 	struct dp_peer *peer = NULL;
 
-
 	QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_HIGH,
 		"peer_map_event (soc:%p): peer_id %di, hw_peer_id %d, peer_mac "
 		"%02x:%02x:%02x:%02x:%02x:%02x, vdev_id %d\n", soc, peer_id,
@@ -506,18 +769,12 @@ dp_rx_peer_map_handler(void *soc_handle, uint16_t peer_id, uint16_t hw_peer_id,
 	 * in this case just add the ast entry to the existing
 	 * peer ast_list.
 	 */
-	if (!peer) {
-		dp_peer_find_add_id(soc, peer_mac_addr, peer_id,
-				hw_peer_id, vdev_id);
-		if (soc->cdp_soc.ol_ops->peer_map_event) {
-			soc->cdp_soc.ol_ops->peer_map_event(soc->osif_soc,
-					peer_id, hw_peer_id, vdev_id, peer_mac_addr);
-		}
+	if (!peer)
+		peer = dp_peer_find_add_id(soc, peer_mac_addr, peer_id,
+					hw_peer_id, vdev_id);
 
-	} else {
-		dp_peer_add_ast(soc, peer, peer_mac_addr,
-				hw_peer_id, vdev_id);
-	}
+	dp_peer_map_ast(soc, peer, peer_mac_addr,
+			hw_peer_id, vdev_id);
 }
 
 void
@@ -565,6 +822,7 @@ dp_peer_find_detach(struct dp_soc *soc)
 {
 	dp_peer_find_map_detach(soc);
 	dp_peer_find_hash_detach(soc);
+	dp_peer_ast_hash_detach(soc);
 }
 
 static void dp_rx_tid_update_cb(struct dp_soc *soc, void *cb_ctxt,

+ 17 - 0
dp/wifi3.0/dp_peer.h

@@ -54,6 +54,23 @@ void dp_rx_sec_ind_handler(void *soc_handle, uint16_t peer_id,
 uint8_t dp_get_peer_mac_addr_frm_id(struct cdp_soc_t *soc_handle,
 		uint16_t peer_id, uint8_t *peer_mac);
 
+#ifdef FEATURE_WDS
+int dp_peer_add_ast(struct dp_soc *soc, struct dp_peer *peer,
+		uint8_t *mac_addr, bool is_self);
+void dp_peer_del_ast(struct dp_soc *soc,
+		struct dp_ast_entry *ast_entry);
+#else
+static inline int dp_peer_add_ast(struct dp_soc *soc, struct dp_peer *peer,
+		uint8_t *mac_addr, bool is_self)
+{
+	return 0;
+}
+static inline void dp_peer_del_ast(struct dp_soc *soc,
+		struct dp_ast_entry *ast_entry)
+{
+}
+#endif
+
 #ifdef DP_LFR
 /*
  * dp_get_vdev_from_soc_vdev_id_wifi3() -

+ 45 - 24
dp/wifi3.0/dp_rx.h

@@ -344,7 +344,7 @@ void dp_rx_add_to_free_desc_list(union dp_rx_desc_list_elem_t **head,
  *
  * Return: void:
  */
-#ifndef CONFIG_MCL
+#ifdef FEATURE_WDS
 static inline void
 dp_rx_wds_srcport_learn(struct dp_soc *soc,
 			 uint8_t *rx_tlv_hdr,
@@ -359,40 +359,61 @@ dp_rx_wds_srcport_learn(struct dp_soc *soc,
 	memcpy(wds_src_mac, (qdf_nbuf_data(nbuf) + IEEE80211_ADDR_LEN),
 		IEEE80211_ADDR_LEN);
 
-	if (!hal_rx_msdu_end_sa_is_valid_get(rx_tlv_hdr)) {
-		ret = soc->cdp_soc.ol_ops->peer_add_wds_entry(
-						ta_peer->vdev->pdev->osif_pdev,
-						wds_src_mac,
-						ta_peer->mac_addr.raw,
-						flags);
-	} else if (sa_sw_peer_id != ta_peer->peer_ids[0]) {
-		ret = soc->cdp_soc.ol_ops->peer_update_wds_entry(
-						ta_peer->vdev->pdev->osif_pdev,
-						wds_src_mac,
-						ta_peer->mac_addr.raw,
-						flags);
+	if (qdf_unlikely(!hal_rx_msdu_end_sa_is_valid_get(rx_tlv_hdr))) {
+		if (!dp_peer_add_ast(soc, ta_peer, wds_src_mac, 0)) {
+			ret = soc->cdp_soc.ol_ops->peer_add_wds_entry(
+					ta_peer->vdev->pdev->osif_pdev,
+					wds_src_mac,
+					ta_peer->mac_addr.raw,
+					flags);
+		}
+	} else {
+		/*
+		 * Get the AST entry from HW SA index and mark it as active
+		 */
+		struct dp_ast_entry *ast;
+		uint16_t sa_idx = hal_rx_msdu_end_sa_idx_get(rx_tlv_hdr);
+		ast = soc->ast_table[sa_idx];
+
+		/*
+		 * Ensure we are updating the right AST entry by
+		 * validating ast_idx.
+		 * There is a possibility we might arrive here without
+		 * AST MAP event , so this check is mandatory
+		 */
+		if (ast && (ast->ast_idx == sa_idx)) {
+			ast->is_active = TRUE;
+		}
+
+		if (sa_sw_peer_id != ta_peer->peer_ids[0]) {
+			ret = soc->cdp_soc.ol_ops->peer_update_wds_entry(
+					ta_peer->vdev->pdev->osif_pdev,
+					wds_src_mac,
+					ta_peer->mac_addr.raw,
+					flags);
+		}
 	}
 	return;
 }
 #else
-static inline void
+	static inline void
 dp_rx_wds_srcport_learn(struct dp_soc *soc,
-			 uint8_t *rx_tlv_hdr,
-			 struct dp_peer *ta_peer,
-			 qdf_nbuf_t nbuf)
+		uint8_t *rx_tlv_hdr,
+		struct dp_peer *ta_peer,
+		qdf_nbuf_t nbuf)
 {
 }
 #endif
 
 uint8_t dp_rx_process_invalid_peer(struct dp_soc *soc, qdf_nbuf_t nbuf);
 #define DP_RX_LIST_APPEND(head, tail, elem) \
-do {                                                \
-	if (!(head)) {                              \
-		(head) = (elem);                    \
-	} else {                                    \
-		qdf_nbuf_set_next((tail), (elem));  \
-	}                                           \
-	(tail) = (elem);                            \
+	do {                                                \
+		if (!(head)) {                              \
+			(head) = (elem);                    \
+		} else {                                    \
+			qdf_nbuf_set_next((tail), (elem));  \
+		}                                           \
+		(tail) = (elem);                            \
 	qdf_nbuf_set_next((tail), NULL);            \
 } while (0)
 

+ 60 - 17
dp/wifi3.0/dp_types.h

@@ -104,6 +104,15 @@ union dp_tx_desc_list_elem_t;
 struct dp_soc;
 union dp_rx_desc_list_elem_t;
 
+#define DP_PDEV_ITERATE_VDEV_LIST(_pdev, _vdev) \
+	TAILQ_FOREACH((_vdev), &(_pdev)->vdev_list, vdev_list_elem)
+
+#define DP_VDEV_ITERATE_PEER_LIST(_vdev, _peer) \
+	TAILQ_FOREACH((_peer), &(_vdev)->peer_list, peer_list_elem)
+
+#define DP_PEER_ITERATE_ASE_LIST(_peer, _ase) \
+	TAILQ_FOREACH((_ase), &peer->ast_entry_list, ase_list_elem)
+
 #define DP_MUTEX_TYPE qdf_spinlock_t
 
 #define DP_FRAME_IS_MULTICAST(_a)  (*(_a) & 0x01)
@@ -380,12 +389,48 @@ struct reo_desc_list_node {
 	struct dp_rx_tid rx_tid;
 };
 
+#define DP_MAC_ADDR_LEN 6
+union dp_align_mac_addr {
+	uint8_t raw[DP_MAC_ADDR_LEN];
+	struct {
+		uint16_t bytes_ab;
+		uint16_t bytes_cd;
+		uint16_t bytes_ef;
+	} align2;
+	struct {
+		uint32_t bytes_abcd;
+		uint16_t bytes_ef;
+	} align4;
+	struct {
+		uint16_t bytes_ab;
+		uint32_t bytes_cdef;
+	} align4_2;
+};
+
+/*
+ * dp_ast_entry
+ *
+ * @ast_idx: Hardware AST Index
+ * @mac_addr:  MAC Address for this AST entry
+ * @peer: Next Hop peer (for non-WDS nodes, this will be point to
+ *        associated peer with this MAC address)
+ * @next_hop: Set to 1 if this is for a WDS node
+ * @is_active: flag to indicate active data traffic on this node
+ *             (used for aging out/expiry)
+ * @is_static: flag to indicate static entry (should not be expired)
+ * @ase_list_elem: node in peer AST list
+ * @hash_list_elem: node in soc AST hash list (mac address used as hash)
+ */
 struct dp_ast_entry {
 	uint16_t ast_idx;
-	uint8_t mac_addr[DP_MAC_ADDR_LEN];
-	uint8_t next_hop;
+	/* MAC address */
+	union dp_align_mac_addr mac_addr;
 	struct dp_peer *peer;
-	TAILQ_ENTRY(dp_ast_entry) ast_entry_elem;
+	bool next_hop;
+	bool is_active;
+	bool is_static;
+	TAILQ_ENTRY(dp_ast_entry) ase_list_elem;
+	TAILQ_ENTRY(dp_ast_entry) hash_list_elem;
 };
 
 struct mect_entry {
@@ -617,6 +662,14 @@ struct dp_soc {
 	bool process_tx_status;
 
 	struct dp_ast_entry *ast_table[WLAN_UMAC_PSOC_MAX_PEERS];
+	struct {
+		unsigned mask;
+		unsigned idx_bits;
+		TAILQ_HEAD(, dp_ast_entry) * bins;
+	} ast_hash;
+
+	qdf_spinlock_t ast_lock;
+	qdf_timer_t wds_aging_timer;
 
 #ifdef DP_INTR_POLL_BASED
 	/*interrupt timer*/
@@ -668,19 +721,6 @@ enum dp_nac_param_cmd {
 	/* IEEE80211_NAC_PARAM_LIST */
 	DP_NAC_PARAM_LIST,
 };
-#define DP_MAC_ADDR_LEN 6
-union dp_align_mac_addr {
-	uint8_t raw[DP_MAC_ADDR_LEN];
-	struct {
-		uint16_t bytes_ab;
-		uint16_t bytes_cd;
-		uint16_t bytes_ef;
-	} align2;
-	struct {
-		uint32_t bytes_abcd;
-		uint16_t bytes_ef;
-	} align4;
-};
 
 /**
  * struct dp_neighbour_peer - neighbour peer list type for smart mesh
@@ -920,6 +960,9 @@ struct dp_vdev {
 	/* WDS enabled */
 	bool wds_enabled;
 
+	/* WDS Aging timer period */
+	uint32_t wds_aging_timer_val;
+
 	/* NAWDS enabled */
 	bool nawds_enabled;
 
@@ -968,7 +1011,7 @@ struct dp_peer {
 	/* VDEV to which this peer is associated */
 	struct dp_vdev *vdev;
 
-	struct dp_ast_entry self_ast_entry;
+	struct dp_ast_entry *self_ast_entry;
 
 	qdf_atomic_t ref_cnt;
 

+ 25 - 0
dp/wifi3.0/hal_rx.h

@@ -813,6 +813,31 @@ hal_rx_msdu_end_l3_hdr_padding_get(uint8_t *buf)
 	return l3_header_padding;
 }
 
+#define HAL_RX_MSDU_END_SA_IDX_GET(_rx_msdu_end)	\
+	(_HAL_MS((*_OFFSET_TO_WORD_PTR(_rx_msdu_end,	\
+		RX_MSDU_END_13_SA_IDX_OFFSET)),	\
+		RX_MSDU_END_13_SA_IDX_MASK,		\
+		RX_MSDU_END_13_SA_IDX_LSB))
+
+ /**
+ * hal_rx_msdu_end_sa_idx_get(): API to get the
+ * sa_idx from rx_msdu_end TLV
+ *
+ * @ buf: pointer to the start of RX PKT TLV headers
+ * Return: sa_idx (SA AST index)
+ */
+static inline uint16_t
+hal_rx_msdu_end_sa_idx_get(uint8_t *buf)
+{
+	struct rx_pkt_tlvs *pkt_tlvs = (struct rx_pkt_tlvs *)buf;
+	struct rx_msdu_end *msdu_end = &pkt_tlvs->msdu_end_tlv.rx_msdu_end;
+	uint8_t sa_idx;
+
+	sa_idx = HAL_RX_MSDU_END_SA_IDX_GET(msdu_end);
+
+	return sa_idx;
+}
+
 #define HAL_RX_MSDU_END_SA_IS_VALID_GET(_rx_msdu_end)	\
 	(_HAL_MS((*_OFFSET_TO_WORD_PTR(_rx_msdu_end,	\
 		RX_MSDU_END_5_SA_IS_VALID_OFFSET)),	\