mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-13 08:07:32 -04:00
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
This commit is contained in:
parent
561cdb9c4a
commit
f74d6f8a97
6 changed files with 186 additions and 36 deletions
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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_ */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in a new issue