diff options
| author | Deepak Dhamdhere <ddhamdhe@codeaurora.org> | 2016-09-16 02:47:01 -0700 |
|---|---|---|
| committer | qcabuildsw <qcabuildsw@localhost> | 2016-10-07 21:18:46 -0700 |
| commit | f74d6f8a97f72758e2e05a5458a6ed9c0a8b79ec (patch) | |
| tree | 407308cf4c727669138c8c3f3aa82131088edcb8 | |
| parent | 561cdb9c4ab836a4da5b6b054803def86327eacc (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.c | 62 | ||||
| -rw-r--r-- | core/dp/txrx/ol_txrx.h | 1 | ||||
| -rw-r--r-- | core/dp/txrx/ol_txrx_peer_find.c | 134 | ||||
| -rw-r--r-- | core/dp/txrx/ol_txrx_peer_find.h | 3 | ||||
| -rw-r--r-- | core/dp/txrx/ol_txrx_types.h | 12 | ||||
| -rw-r--r-- | core/wma/src/wma_dev_if.c | 10 |
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, |
