diff options
| author | Mohit Khanna <mkhannaqca@codeaurora.org> | 2016-04-14 16:14:08 -0700 |
|---|---|---|
| committer | Vishwajith Upendra <vishwaji@codeaurora.org> | 2016-04-21 20:23:50 -0700 |
| commit | 0696eef313b507aacbd7b3864a52869013851e5f (patch) | |
| tree | 02ecb05c752fa2c9e1d7a97f0ec38177444e3d69 | |
| parent | bb116f2d4f958683f2ce560737d8947b894b62c4 (diff) | |
qcacld-3.0: Fix RX data, peer unmap race condition
During peer unmap handler, while the peer is being deleted, there is a
possible race condition if the OL Rx thread is processing RX packets
and accesses the peer structure after its contents have been nulled.
Remove race condition by -
* Flush all RX packets in ol_txrx_peer_detach function which happens
before peer unmap event is received from firmware
* Avoid use of peer data structures (for example peer->local_id)
outside of peer->info_lock in ol_rx_data_cb function. Use cached
local copies of peer data structures instead
Crash signature due to the race condition:
wlan: [0:E :CDF] TXRX: Deleting peer ffffffc012fd13c0
(02:a0:c6:81:f8:c0)
Unable to handle kernel paging request at virtual address 400000001
pgd = ffffffc0018b4000
[400000001] *pgd=0000000000000000, *pud=0000000000000000
Internal error: Oops: 96000005 [#1] PREEMPT SMP
Modules linked in: wlan(O) [last unloaded: wlan]
CPU: 1 PID: 29506 Comm: cds_ol_rx_threa Tainted: G W O
3.18.20-g5222edf-13780-g2219ed2 #1
Hardware name: Qualcomm Technologies, Inc. MSM 8996 v3 + PMI8996 CD (DT)
task: ffffffc09350d400 ti: ffffffc0556a4000 task.ti: ffffffc0556a4000
PC is at hdd_rx_packet_cbk+0x84/0x224 [wlan]
LR is at hdd_rx_packet_cbk+0x48/0x224 [wlan]
pc : [<ffffffbffdd55b5c>] lr : [<ffffffbffdd55b20>] pstate: 80000145
Change-Id: I4b32313024ec214f33dcdcfc401aadfa8af9d692
CRs-Fixed: 1002081
| -rw-r--r-- | core/cds/inc/cds_sched.h | 65 | ||||
| -rw-r--r-- | core/dp/txrx/ol_txrx.c | 109 |
2 files changed, 125 insertions, 49 deletions
diff --git a/core/cds/inc/cds_sched.h b/core/cds/inc/cds_sched.h index 7325b6155e26..46908e6701f6 100644 --- a/core/cds/inc/cds_sched.h +++ b/core/cds/inc/cds_sched.h @@ -296,7 +296,6 @@ typedef struct _cds_context_type { /*--------------------------------------------------------------------------- Function declarations and documenation ---------------------------------------------------------------------------*/ - #ifdef QCA_CONFIG_SMP /*--------------------------------------------------------------------------- \brief cds_drop_rxpkt_by_staid() - API to drop pending Rx packets for a sta @@ -357,6 +356,70 @@ void cds_free_ol_rx_pkt(p_cds_sched_context pSchedContext, \sa cds_free_ol_rx_pkt_freeq() -------------------------------------------------------------------------*/ void cds_free_ol_rx_pkt_freeq(p_cds_sched_context pSchedContext); +#else +/** + * cds_drop_rxpkt_by_staid() - api to drop pending rx packets for a sta + * @pSchedContext: Pointer to the global CDS Sched Context + * @staId: Station Id + * + * This api drops queued packets for a station, to drop all the pending + * packets the caller has to send WLAN_MAX_STA_COUNT as staId. + * + * Return: none + */ +static inline +void cds_drop_rxpkt_by_staid(p_cds_sched_context pSchedContext, uint16_t staId) +{ +} + +/** + * cds_indicate_rxpkt() - API to Indicate rx data packet + * @pSchedContext: pointer to CDS Sched Context + * @pkt: CDS OL RX pkt pointer containing to RX data message buffer + * + * Return: none + */ +static inline +void cds_indicate_rxpkt(p_cds_sched_context pSchedContext, + struct cds_ol_rx_pkt *pkt) +{ +} + +/** + * cds_alloc_ol_rx_pkt() - API to return next available cds message + * @pSchedContext: pointer to CDS Sched Context + * + * Return: none + */ +static inline +struct cds_ol_rx_pkt *cds_alloc_ol_rx_pkt(p_cds_sched_context pSchedContext) +{ +} + +/** + * cds_free_ol_rx_pkt() - API to release cds message to the freeq + * @pSchedContext: pointer to CDS Sched Context + * @pkt: CDS message buffer to be returned to free queue + * + * Return: none + */ +static inline +void cds_free_ol_rx_pkt(p_cds_sched_context pSchedContext, + struct cds_ol_rx_pkt *pkt) +{ +} + +/** + * cds_free_ol_rx_pkt_freeq() - Free cds buffer free queue + * @pSchedContext: pointer to CDS Sched Context + * @pkt: CDS message buffer to be returned to free queue + * + * Return: none + */ +static inline +void cds_free_ol_rx_pkt_freeq(p_cds_sched_context pSchedContext) +{ +} #endif /*--------------------------------------------------------------------------- diff --git a/core/dp/txrx/ol_txrx.c b/core/dp/txrx/ol_txrx.c index 2316b1e5cd4f..c2d85772e6df 100644 --- a/core/dp/txrx/ol_txrx.c +++ b/core/dp/txrx/ol_txrx.c @@ -2227,6 +2227,62 @@ void ol_txrx_peer_unref_delete(ol_txrx_peer_handle peer) } /** + * ol_txrx_clear_peer_internal() - ol internal function to clear peer + * @peer: pointer to ol txrx peer structure + * + * Return: QDF Status + */ +static QDF_STATUS +ol_txrx_clear_peer_internal(struct ol_txrx_peer_t *peer) +{ + p_cds_sched_context sched_ctx = get_cds_sched_ctxt(); + /* Drop pending Rx frames in CDS */ + if (sched_ctx) + cds_drop_rxpkt_by_staid(sched_ctx, peer->local_id); + + /* Purge the cached rx frame queue */ + ol_txrx_flush_rx_frames(peer, 1); + + qdf_spin_lock_bh(&peer->peer_info_lock); + peer->vdev->rx = NULL; + peer->state = OL_TXRX_PEER_STATE_DISC; + qdf_spin_unlock_bh(&peer->peer_info_lock); + + return QDF_STATUS_SUCCESS; +} + +/** + * ol_txrx_clear_peer() - clear peer + * @sta_id: sta id + * + * Return: QDF Status + */ +QDF_STATUS ol_txrx_clear_peer(uint8_t sta_id) +{ + struct ol_txrx_peer_t *peer; + struct ol_txrx_pdev_t *pdev = cds_get_context(QDF_MODULE_ID_TXRX); + + if (!pdev) { + TXRX_PRINT(TXRX_PRINT_LEVEL_ERR, "%s: Unable to find pdev!", + __func__); + return QDF_STATUS_E_FAILURE; + } + + if (sta_id >= WLAN_MAX_STA_COUNT) { + TXRX_PRINT(TXRX_PRINT_LEVEL_ERR, "Invalid sta id %d", sta_id); + return QDF_STATUS_E_INVAL; + } + + + peer = ol_txrx_peer_find_by_local_id(pdev, sta_id); + if (!peer) + return QDF_STATUS_E_FAULT; + + return ol_txrx_clear_peer_internal(peer); + +} + +/** * ol_txrx_peer_detach - Delete a peer's data object. * @data_peer - the object to delete * @@ -2245,6 +2301,8 @@ void ol_txrx_peer_detach(ol_txrx_peer_handle peer) peer->valid = 0; + /* flush all rx packets before clearing up the peer local_id */ + ol_txrx_clear_peer_internal(peer); ol_txrx_local_peer_id_free(peer->vdev->pdev, peer); /* debug print to dump rx reorder state */ @@ -2256,7 +2314,6 @@ void ol_txrx_peer_detach(ol_txrx_peer_handle 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]); - ol_txrx_flush_rx_frames(peer, 1); if (peer->vdev->last_real_peer == peer) peer->vdev->last_real_peer = NULL; @@ -3455,6 +3512,7 @@ static void ol_rx_data_cb(struct ol_txrx_pdev_t *pdev, qdf_nbuf_t buf_list, uint16_t staid) { void *cds_ctx = cds_get_global_context(); + void *osif_dev; qdf_nbuf_t buf, next_buf; QDF_STATUS ret; ol_txrx_rx_fp data_rx = NULL; @@ -3478,6 +3536,7 @@ static void ol_rx_data_cb(struct ol_txrx_pdev_t *pdev, } data_rx = peer->vdev->rx; + osif_dev = peer->vdev->osif_dev; qdf_spin_unlock_bh(&peer->peer_info_lock); qdf_spin_lock_bh(&peer->bufq_lock); @@ -3492,7 +3551,7 @@ static void ol_rx_data_cb(struct ol_txrx_pdev_t *pdev, while (buf) { next_buf = qdf_nbuf_queue_next(buf); qdf_nbuf_set_next(buf, NULL); /* Add NULL terminator */ - ret = data_rx(peer->vdev->osif_dev, buf); + ret = data_rx(osif_dev, buf); if (ret != QDF_STATUS_SUCCESS) { TXRX_PRINT(TXRX_PRINT_LEVEL_ERR, "Frame Rx to HDD failed"); qdf_nbuf_free(buf); @@ -3661,52 +3720,6 @@ QDF_STATUS ol_txrx_register_peer(struct ol_txrx_desc_type *sta_desc) } /** - * ol_txrx_clear_peer() - clear peer - * @sta_id: sta id - * - * Return: QDF Status - */ -QDF_STATUS ol_txrx_clear_peer(uint8_t sta_id) -{ - struct ol_txrx_peer_t *peer; - struct ol_txrx_pdev_t *pdev = cds_get_context(QDF_MODULE_ID_TXRX); - - if (!pdev) { - TXRX_PRINT(TXRX_PRINT_LEVEL_ERR, "%s: Unable to find pdev!", - __func__); - return QDF_STATUS_E_FAILURE; - } - - if (sta_id >= WLAN_MAX_STA_COUNT) { - TXRX_PRINT(TXRX_PRINT_LEVEL_ERR, "Invalid sta id %d", sta_id); - return QDF_STATUS_E_INVAL; - } - -#ifdef QCA_CONFIG_SMP - { - p_cds_sched_context sched_ctx = get_cds_sched_ctxt(); - /* Drop pending Rx frames in CDS */ - if (sched_ctx) - cds_drop_rxpkt_by_staid(sched_ctx, sta_id); - } -#endif - - peer = ol_txrx_peer_find_by_local_id(pdev, sta_id); - if (!peer) - return QDF_STATUS_E_FAULT; - - /* Purge the cached rx frame queue */ - ol_txrx_flush_rx_frames(peer, 1); - - qdf_spin_lock_bh(&peer->peer_info_lock); - peer->vdev->rx = NULL; - peer->state = OL_TXRX_PEER_STATE_DISC; - qdf_spin_unlock_bh(&peer->peer_info_lock); - - return QDF_STATUS_SUCCESS; -} - -/** * ol_txrx_register_ocb_peer - Function to register the OCB peer * @cds_ctx: Pointer to the global OS context * @mac_addr: MAC address of the self peer |
