summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDeepak Dhamdhere <ddhamdhe@codeaurora.org>2016-09-16 02:47:01 -0700
committerqcabuildsw <qcabuildsw@localhost>2016-10-07 21:18:46 -0700
commitf74d6f8a97f72758e2e05a5458a6ed9c0a8b79ec (patch)
tree407308cf4c727669138c8c3f3aa82131088edcb8
parent561cdb9c4ab836a4da5b6b054803def86327eacc (diff)
qcacld-3.0: Fix peer unmap/map handling during roaming
Roam_synch indication processing in host performs peer detach/attach operations after firmware has already moved to the new peer. Out of sequence peer unmap and map events can mess up the reference count for reused peer_id values, which can lead to crash. Solution: While detaching a peer during roam sync indication processing, copy its peer_id_ref_cnt in peer_id_to_obj array to new variable in the same peer map for that peer id. Peer is deleted at that point. When the unmap events come in, decrement the old ref_cnt and map events increment the real ref_cnt. Once the old ref_cnt goes to 0, subsequent unmap operations apply to the real peer. CRs-Fixed: 1063177 Change-Id: I9b20f28f17dea1647a213b9f36060109264addf0
-rw-r--r--core/dp/txrx/ol_txrx.c62
-rw-r--r--core/dp/txrx/ol_txrx.h1
-rw-r--r--core/dp/txrx/ol_txrx_peer_find.c134
-rw-r--r--core/dp/txrx/ol_txrx_peer_find.h3
-rw-r--r--core/dp/txrx/ol_txrx_types.h12
-rw-r--r--core/wma/src/wma_dev_if.c10
6 files changed, 186 insertions, 36 deletions
diff --git a/core/dp/txrx/ol_txrx.c b/core/dp/txrx/ol_txrx.c
index a80f6ac84aa8..8f1a565cba35 100644
--- a/core/dp/txrx/ol_txrx.c
+++ b/core/dp/txrx/ol_txrx.c
@@ -1224,12 +1224,6 @@ ol_txrx_pdev_post_attach(ol_txrx_pdev_handle pdev)
c_element->tx_desc.htt_frag_desc = htt_frag_desc;
c_element->tx_desc.htt_frag_desc_paddr = frag_paddr;
}
- QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW,
- "%s:%d - %d FRAG VA 0x%p FRAG PA 0x%llx",
- __func__, __LINE__, i,
- c_element->tx_desc.htt_frag_desc,
- (long long unsigned int)
- c_element->tx_desc.htt_frag_desc_paddr);
#ifdef QCA_SUPPORT_TXDESC_SANITY_CHECKS
c_element->tx_desc.pkt_type = 0xff;
#ifdef QCA_COMPUTE_TX_DELAY
@@ -2790,15 +2784,15 @@ void ol_txrx_peer_unref_delete(ol_txrx_peer_handle peer)
* concurrently with the empty check.
*/
qdf_spin_lock_bh(&pdev->peer_ref_mutex);
+
if (qdf_atomic_dec_and_test(&peer->ref_cnt)) {
u_int16_t peer_id;
TXRX_PRINT(TXRX_PRINT_LEVEL_ERR,
- "Deleting peer %p (%02x:%02x:%02x:%02x:%02x:%02x)",
+ "Deleting peer %p (%pM) ref_cnt %d\n",
peer,
- peer->mac_addr.raw[0], peer->mac_addr.raw[1],
- peer->mac_addr.raw[2], peer->mac_addr.raw[3],
- peer->mac_addr.raw[4], peer->mac_addr.raw[5]);
+ peer->mac_addr.raw,
+ qdf_atomic_read(&peer->ref_cnt));
peer_id = peer->local_id;
/* remove the reference to the peer from the hash table */
@@ -2949,14 +2943,15 @@ QDF_STATUS ol_txrx_clear_peer(uint8_t sta_id)
}
/**
- * ol_txrx_peer_detach - Delete a peer's data object.
- * @data_peer - the object to delete
+ * ol_txrx_peer_detach() - Delete a peer's data object.
+ * @peer - the object to detach
*
* When the host's control SW disassociates a peer, it calls
- * this function to delete the peer's data object. The reference
+ * this function to detach and delete the peer. The reference
* stored in the control peer object to the data peer
* object (set up by a call to ol_peer_store()) is provided.
*
+ * Return: None
*/
void ol_txrx_peer_detach(ol_txrx_peer_handle peer)
{
@@ -2992,9 +2987,12 @@ void ol_txrx_peer_detach(ol_txrx_peer_handle peer)
qdf_spinlock_destroy(&peer->peer_info_lock);
qdf_spinlock_destroy(&peer->bufq_lock);
- /* set delete_in_progress to identify that wma
- * is waiting for unmap massage for this peer */
+ /*
+ * set delete_in_progress to identify that wma
+ * is waiting for unmap massage for this peer
+ */
qdf_atomic_set(&peer->delete_in_progress, 1);
+
/*
* Remove the reference added during peer_attach.
* The peer will still be left allocated until the
@@ -3004,6 +3002,40 @@ void ol_txrx_peer_detach(ol_txrx_peer_handle peer)
ol_txrx_peer_unref_delete(peer);
}
+/**
+ * ol_txrx_peer_detach_force_delete() - Detach and delete a peer's data object
+ * @peer - the object to detach
+ *
+ * Detach a peer and force the peer object to be removed. It is called during
+ * roaming scenario when the firmware has already deleted a peer.
+ * Peer object is freed immediately to avoid duplicate peers during roam sync
+ * indication processing.
+ *
+ * Return: None
+ */
+void ol_txrx_peer_detach_force_delete(ol_txrx_peer_handle peer)
+{
+ ol_txrx_pdev_handle pdev = peer->vdev->pdev;
+
+ TXRX_PRINT(TXRX_PRINT_LEVEL_ERR, "%s peer %p, peer->ref_cnt %d",
+ __func__, peer, qdf_atomic_read(&peer->ref_cnt));
+
+ /* Clear the peer_id_to_obj map entries */
+ qdf_spin_lock_bh(&pdev->peer_map_unmap_lock);
+ ol_txrx_peer_remove_obj_map_entries(pdev, peer);
+ qdf_spin_unlock_bh(&pdev->peer_map_unmap_lock);
+
+ /*
+ * Set ref_cnt = 1 so that ol_txrx_peer_unref_delete() called by
+ * ol_txrx_peer_detach() will actually delete this peer entry properly.
+ */
+ qdf_spin_lock_bh(&pdev->peer_ref_mutex);
+ qdf_atomic_set(&peer->ref_cnt, 1);
+ qdf_spin_unlock_bh(&pdev->peer_ref_mutex);
+
+ ol_txrx_peer_detach(peer);
+}
+
ol_txrx_peer_handle
ol_txrx_peer_find_by_addr(struct ol_txrx_pdev_t *pdev, uint8_t *peer_mac_addr)
{
diff --git a/core/dp/txrx/ol_txrx.h b/core/dp/txrx/ol_txrx.h
index e4d463db0e9c..daa4f0fde410 100644
--- a/core/dp/txrx/ol_txrx.h
+++ b/core/dp/txrx/ol_txrx.h
@@ -134,4 +134,5 @@ void htt_pkt_log_init(struct ol_txrx_pdev_t *handle, void *scn);
QDF_STATUS ol_txrx_set_wisa_mode(ol_txrx_vdev_handle vdev,
bool enable);
void ol_txrx_update_mac_id(uint8_t vdev_id, uint8_t mac_id);
+void ol_txrx_peer_detach_force_delete(ol_txrx_peer_handle peer);
#endif /* _OL_TXRX__H_ */
diff --git a/core/dp/txrx/ol_txrx_peer_find.c b/core/dp/txrx/ol_txrx_peer_find.c
index 37acb70c3aa8..f43670155242 100644
--- a/core/dp/txrx/ol_txrx_peer_find.c
+++ b/core/dp/txrx/ol_txrx_peer_find.c
@@ -327,14 +327,27 @@ static void ol_txrx_peer_find_map_detach(struct ol_txrx_pdev_t *pdev)
qdf_mem_free(pdev->peer_id_to_obj_map);
}
-static inline void
-ol_txrx_peer_find_add_id(struct ol_txrx_pdev_t *pdev,
- uint8_t *peer_mac_addr, uint16_t peer_id)
+/*
+ * ol_txrx_peer_find_add_id() - Add peer_id entry to peer
+ *
+ * @pdev: Handle to pdev object
+ * @peer_mac_addr: MAC address of peer provided by firmware
+ * @peer_id: peer_id provided by firmware
+ *
+ * Search for peer object for the MAC address, add the peer_id to
+ * its array of peer_id's and update the peer_id_to_obj map entry
+ * for that peer_id. Increment corresponding reference counts.
+ *
+ * Return: None
+ */
+static inline void ol_txrx_peer_find_add_id(struct ol_txrx_pdev_t *pdev,
+ uint8_t *peer_mac_addr, uint16_t peer_id)
{
struct ol_txrx_peer_t *peer;
int status;
int del_peer_ref = 0;
int i;
+ bool found = false;
/* check if there's already a peer object with this MAC address */
peer =
@@ -366,10 +379,18 @@ ol_txrx_peer_find_add_id(struct ol_txrx_pdev_t *pdev,
qdf_atomic_inc
(&pdev->peer_id_to_obj_map[peer_id].peer_id_ref_cnt);
- if (peer->peer_ids[0] == HTT_INVALID_PEER)
+ /* Check if entire peer_id array is empty */
+ for (i = 0; i < MAX_NUM_PEER_ID_PER_PEER; i++) {
+ if (peer->peer_ids[i] != HTT_INVALID_PEER) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
del_peer_ref = 1;
-
status = 1;
+
+ /* find a place in peer_id array and insert peer_id */
for (i = 0; i < MAX_NUM_PEER_ID_PER_PEER; i++) {
if (peer->peer_ids[i] == HTT_INVALID_PEER) {
peer->peer_ids[i] = peer_id;
@@ -506,7 +527,20 @@ void ol_txrx_peer_tx_ready_handler(ol_txrx_pdev_handle pdev, uint16_t peer_id)
#endif
-
+/*
+ * ol_rx_peer_unmap_handler() - Handle peer unmap event from firmware
+ *
+ * @pdev: Handle to pdev pbject
+ * @peer_id: peer_id unmapped by firmware
+ *
+ * Decrement reference count for the peer_id in peer_id_to_obj_map,
+ * decrement reference count in corresponding peer object and clear the entry
+ * in peer's peer_ids array.
+ * In case of unmap events for a peer that is already deleted, just decrement
+ * del_peer_id_ref_cnt.
+ *
+ * Return: None
+ */
void ol_rx_peer_unmap_handler(ol_txrx_pdev_handle pdev, uint16_t peer_id)
{
struct ol_txrx_peer_t *peer;
@@ -518,24 +552,39 @@ void ol_rx_peer_unmap_handler(ol_txrx_pdev_handle pdev, uint16_t peer_id)
return;
}
- qdf_spin_lock(&pdev->peer_map_unmap_lock);
+ qdf_spin_lock_bh(&pdev->peer_map_unmap_lock);
+ if (qdf_atomic_read(
+ &pdev->peer_id_to_obj_map[peer_id].del_peer_id_ref_cnt)) {
+ /* This peer_id belongs to a peer already deleted */
+ qdf_atomic_dec(&pdev->peer_id_to_obj_map[peer_id].
+ del_peer_id_ref_cnt);
+ TXRX_PRINT(TXRX_PRINT_LEVEL_ERR,
+ "%s: Remove the ID %d reference to deleted peer. del_peer_id_ref_cnt %d",
+ __func__, peer_id,
+ qdf_atomic_read(&pdev->peer_id_to_obj_map[peer_id].
+ del_peer_id_ref_cnt));
+ qdf_spin_unlock_bh(&pdev->peer_map_unmap_lock);
+ return;
+ }
peer = pdev->peer_id_to_obj_map[peer_id].peer;
if (peer == NULL) {
- /*
- * Currently peer IDs are assigned for vdevs as well as peers.
- * If the peer ID is for a vdev, then the peer pointer stored
- * in peer_id_to_obj_map will be NULL.
- */
- qdf_spin_unlock(&pdev->peer_map_unmap_lock);
+ /*
+ * Currently peer IDs are assigned for vdevs as well as peers.
+ * If the peer ID is for a vdev, then the peer pointer stored
+ * in peer_id_to_obj_map will be NULL.
+ */
+ TXRX_PRINT(TXRX_PRINT_LEVEL_ERR,
+ "%s: peer not found for peer_id %d",
+ __func__, peer_id);
+ qdf_spin_unlock_bh(&pdev->peer_map_unmap_lock);
return;
}
if (qdf_atomic_dec_and_test
(&pdev->peer_id_to_obj_map[peer_id].peer_id_ref_cnt)) {
pdev->peer_id_to_obj_map[peer_id].peer = NULL;
-
for (i = 0; i < MAX_NUM_PEER_ID_PER_PEER; i++) {
if (peer->peer_ids[i] == peer_id) {
peer->peer_ids[i] = HTT_INVALID_PEER;
@@ -543,9 +592,6 @@ void ol_rx_peer_unmap_handler(ol_txrx_pdev_handle pdev, uint16_t peer_id)
}
}
}
-
- qdf_spin_unlock(&pdev->peer_map_unmap_lock);
-
TXRX_PRINT(TXRX_PRINT_LEVEL_ERR,
"%s: Remove the ID %d reference to peer %p peer_id_ref_cnt %d",
__func__, peer_id, peer,
@@ -557,6 +603,59 @@ void ol_rx_peer_unmap_handler(ol_txrx_pdev_handle pdev, uint16_t peer_id)
* If there are no more references, delete the peer object.
*/
ol_txrx_peer_unref_delete(peer);
+
+ qdf_spin_unlock_bh(&pdev->peer_map_unmap_lock);
+}
+
+/**
+ * ol_txrx_peer_remove_obj_map_entries() - Remove matching pdev peer map entries
+ * @pdev: pdev handle
+ * @peer: peer to be removed
+ *
+ * Return: None
+ */
+void ol_txrx_peer_remove_obj_map_entries(ol_txrx_pdev_handle pdev,
+ struct ol_txrx_peer_t *peer)
+{
+ int i;
+ uint16_t peer_id;
+ int32_t peer_id_ref_cnt;
+
+ for (i = 0; i < MAX_NUM_PEER_ID_PER_PEER; i++) {
+ peer_id = peer->peer_ids[i];
+ if (peer_id == HTT_INVALID_PEER ||
+ pdev->peer_id_to_obj_map[peer_id].peer == NULL) {
+ /* unused peer_id, or object is already dereferenced */
+ continue;
+ }
+ if (pdev->peer_id_to_obj_map[peer_id].peer != peer) {
+ QDF_TRACE(QDF_MODULE_ID_TXRX,
+ QDF_TRACE_LEVEL_ERROR,
+ FL("peer pointer mismatch in peer_id_to_obj"));
+ continue;
+ }
+ peer_id_ref_cnt = qdf_atomic_read(
+ &pdev->peer_id_to_obj_map[peer_id].
+ peer_id_ref_cnt);
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_LOW,
+ FL("peer_id = %d, peer_id_ref_cnt = %d, index = %d"),
+ peer_id, peer_id_ref_cnt, i);
+ /*
+ * Transfer peer_id_ref_cnt into del_peer_id_ref_cnt so that
+ * ol_txrx_peer_unref_delete will decrement del_peer_id_ref_cnt
+ * and any map events will increment peer_id_ref_cnt. Otherwise
+ * accounting will be messed up.
+ *
+ * Add operation will ensure that back to back roaming in the
+ * middle of unmap/map event sequence will be accounted for.
+ */
+ qdf_atomic_add(peer_id_ref_cnt,
+ &pdev->peer_id_to_obj_map[peer_id].del_peer_id_ref_cnt);
+ qdf_atomic_init(&pdev->peer_id_to_obj_map[peer_id].
+ peer_id_ref_cnt);
+ pdev->peer_id_to_obj_map[peer_id].peer = NULL;
+ peer->peer_ids[i] = HTT_INVALID_PEER;
+ }
}
struct ol_txrx_peer_t *ol_txrx_assoc_peer_find(struct ol_txrx_vdev_t *vdev)
@@ -623,4 +722,5 @@ void ol_txrx_peer_find_display(ol_txrx_pdev_handle pdev, int indent)
}
}
}
+
#endif /* if TXRX_DEBUG_LEVEL */
diff --git a/core/dp/txrx/ol_txrx_peer_find.h b/core/dp/txrx/ol_txrx_peer_find.h
index 43c2f141aac1..389c1764a3f5 100644
--- a/core/dp/txrx/ol_txrx_peer_find.h
+++ b/core/dp/txrx/ol_txrx_peer_find.h
@@ -107,6 +107,9 @@ void ol_txrx_peer_find_hash_erase(struct ol_txrx_pdev_t *pdev);
struct ol_txrx_peer_t *ol_txrx_assoc_peer_find(struct ol_txrx_vdev_t *vdev);
+void ol_txrx_peer_remove_obj_map_entries(ol_txrx_pdev_handle pdev,
+ struct ol_txrx_peer_t *peer);
+
#if defined(TXRX_DEBUG_LEVEL) && TXRX_DEBUG_LEVEL > 5
void ol_txrx_peer_find_display(ol_txrx_pdev_handle pdev, int indent);
#else
diff --git a/core/dp/txrx/ol_txrx_types.h b/core/dp/txrx/ol_txrx_types.h
index 26ecfbafe83b..ee1bfb309a37 100644
--- a/core/dp/txrx/ol_txrx_types.h
+++ b/core/dp/txrx/ol_txrx_types.h
@@ -58,7 +58,7 @@
* multicast key the peer uses, and another ID to represent the
* unicast key the peer uses.
*/
-#define MAX_NUM_PEER_ID_PER_PEER 8
+#define MAX_NUM_PEER_ID_PER_PEER 16
#define OL_TXRX_INVALID_NUM_PEERS (-1)
@@ -484,9 +484,19 @@ struct ol_tx_flow_pool_t {
#endif
+/*
+ * struct ol_txrx_peer_id_map - Map of firmware peer_ids to peers on host
+ * @peer: Pointer to peer object
+ * @peer_id_ref_cnt: No. of firmware references to the peer_id
+ * @del_peer_id_ref_cnt: No. of outstanding unmap events for peer_id
+ * after the peer object is deleted on the host.
+ *
+ * peer_id is used as an index into the array of ol_txrx_peer_id_map.
+ */
struct ol_txrx_peer_id_map {
struct ol_txrx_peer_t *peer;
qdf_atomic_t peer_id_ref_cnt;
+ qdf_atomic_t del_peer_id_ref_cnt;
};
/*
diff --git a/core/wma/src/wma_dev_if.c b/core/wma/src/wma_dev_if.c
index 6a7d2084b79c..49a7a0993cfa 100644
--- a/core/wma/src/wma_dev_if.c
+++ b/core/wma/src/wma_dev_if.c
@@ -1079,9 +1079,13 @@ void wma_remove_peer(tp_wma_handle wma, uint8_t *bssid,
wma->interfaces[vdev_id].peer_count);
return;
}
- if (peer)
- ol_txrx_peer_detach(peer);
+ if (peer) {
+ if (roam_synch_in_progress)
+ ol_txrx_peer_detach_force_delete(peer);
+ else
+ ol_txrx_peer_detach(peer);
+ }
peer_mac_addr = ol_txrx_peer_get_peer_mac_addr(peer);
if (peer_mac_addr == NULL) {
WMA_LOGE("%s: peer mac addr is NULL, Can't remove peer with peer_addr %pM vdevid %d peer_count %d",
@@ -1146,8 +1150,8 @@ QDF_STATUS wma_create_peer(tp_wma_handle wma, ol_txrx_pdev_handle pdev,
WMA_LOGE("%s : Unable to attach peer %pM", __func__, peer_addr);
goto err;
}
- if (roam_synch_in_progress) {
+ if (roam_synch_in_progress) {
WMA_LOGE("%s: LFR3: Created peer %p with peer_addr %pM vdev_id %d,"
"peer_count - %d",
__func__, peer, peer_addr, vdev_id,