qcacmn: fix TX TDLS discover frame nbuf leak issue
crash scenario: (1) during dp_vdev_detach_wifi3(), it will reset related outstanding TX desc vdev pointer to NULL. (2) In the meantime, if this vdev TX completion is received from HW, dp_non_std_tx_comp_free_buff() do nothing for nbuf due to null vdev, leak will happen. add error handling in dp_non_std_tx_comp_free_buff() to fix it. Change-Id: I942a3d690711c60e8299d86562f08f0fb77f0b32 CRs-Fixed: 2670186
This commit is contained in:
@@ -1528,40 +1528,84 @@ static void dp_tx_update_tdls_flags(struct dp_tx_desc_s *tx_desc)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* dp_non_std_tx_comp_free_buff() - Free the non std tx packet buffer
|
* dp_non_std_tx_comp_free_buff() - Free the non std tx packet buffer
|
||||||
|
* @soc: dp_soc handle
|
||||||
* @tx_desc: TX descriptor
|
* @tx_desc: TX descriptor
|
||||||
* @vdev: datapath vdev handle
|
* @vdev: datapath vdev handle
|
||||||
*
|
*
|
||||||
* Return: None
|
* Return: None
|
||||||
*/
|
*/
|
||||||
static void dp_non_std_tx_comp_free_buff(struct dp_tx_desc_s *tx_desc,
|
static void dp_non_std_tx_comp_free_buff(struct dp_soc *soc,
|
||||||
|
struct dp_tx_desc_s *tx_desc,
|
||||||
struct dp_vdev *vdev)
|
struct dp_vdev *vdev)
|
||||||
{
|
{
|
||||||
struct hal_tx_completion_status ts = {0};
|
struct hal_tx_completion_status ts = {0};
|
||||||
qdf_nbuf_t nbuf = tx_desc->nbuf;
|
qdf_nbuf_t nbuf = tx_desc->nbuf;
|
||||||
|
|
||||||
if (qdf_unlikely(!vdev)) {
|
if (qdf_unlikely(!vdev)) {
|
||||||
dp_err("vdev is null!");
|
dp_err_rl("vdev is null!");
|
||||||
return;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
hal_tx_comp_get_status(&tx_desc->comp, &ts, vdev->pdev->soc->hal_soc);
|
hal_tx_comp_get_status(&tx_desc->comp, &ts, vdev->pdev->soc->hal_soc);
|
||||||
if (vdev->tx_non_std_data_callback.func) {
|
if (vdev->tx_non_std_data_callback.func) {
|
||||||
qdf_nbuf_set_next(tx_desc->nbuf, NULL);
|
qdf_nbuf_set_next(nbuf, NULL);
|
||||||
vdev->tx_non_std_data_callback.func(
|
vdev->tx_non_std_data_callback.func(
|
||||||
vdev->tx_non_std_data_callback.ctxt,
|
vdev->tx_non_std_data_callback.ctxt,
|
||||||
nbuf, ts.status);
|
nbuf, ts.status);
|
||||||
return;
|
return;
|
||||||
|
} else {
|
||||||
|
dp_err_rl("callback func is null");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
error:
|
||||||
|
qdf_nbuf_unmap_single(soc->osdev, nbuf, QDF_DMA_TO_DEVICE);
|
||||||
|
qdf_nbuf_free(nbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dp_tx_msdu_single_map() - do nbuf map
|
||||||
|
* @vdev: DP vdev handle
|
||||||
|
* @tx_desc: DP TX descriptor pointer
|
||||||
|
* @nbuf: skb pointer
|
||||||
|
*
|
||||||
|
* For TDLS frame, use qdf_nbuf_map_single() to align with the unmap
|
||||||
|
* operation done in other component.
|
||||||
|
*
|
||||||
|
* Return: QDF_STATUS
|
||||||
|
*/
|
||||||
|
static inline QDF_STATUS dp_tx_msdu_single_map(struct dp_vdev *vdev,
|
||||||
|
struct dp_tx_desc_s *tx_desc,
|
||||||
|
qdf_nbuf_t nbuf)
|
||||||
|
{
|
||||||
|
if (qdf_likely(!(tx_desc->flags & DP_TX_DESC_FLAG_TDLS_FRAME)))
|
||||||
|
return qdf_nbuf_map_nbytes_single(vdev->osdev,
|
||||||
|
nbuf,
|
||||||
|
QDF_DMA_TO_DEVICE,
|
||||||
|
nbuf->len);
|
||||||
|
else
|
||||||
|
return qdf_nbuf_map_single(vdev->osdev, nbuf,
|
||||||
|
QDF_DMA_TO_DEVICE);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static inline void dp_tx_update_tdls_flags(struct dp_tx_desc_s *tx_desc)
|
static inline void dp_tx_update_tdls_flags(struct dp_tx_desc_s *tx_desc)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void dp_non_std_tx_comp_free_buff(struct dp_tx_desc_s *tx_desc,
|
static inline void dp_non_std_tx_comp_free_buff(struct dp_soc *soc,
|
||||||
|
struct dp_tx_desc_s *tx_desc,
|
||||||
struct dp_vdev *vdev)
|
struct dp_vdev *vdev)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline QDF_STATUS dp_tx_msdu_single_map(struct dp_vdev *vdev,
|
||||||
|
struct dp_tx_desc_s *tx_desc,
|
||||||
|
qdf_nbuf_t nbuf)
|
||||||
|
{
|
||||||
|
return qdf_nbuf_map_nbytes_single(vdev->osdev,
|
||||||
|
nbuf,
|
||||||
|
QDF_DMA_TO_DEVICE,
|
||||||
|
nbuf->len);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1660,11 +1704,9 @@ dp_tx_send_msdu_single(struct dp_vdev *vdev, qdf_nbuf_t nbuf,
|
|||||||
HTT_TX_TCL_METADATA_VALID_HTT_SET(htt_tcl_metadata, 1);
|
HTT_TX_TCL_METADATA_VALID_HTT_SET(htt_tcl_metadata, 1);
|
||||||
|
|
||||||
if (qdf_unlikely(QDF_STATUS_SUCCESS !=
|
if (qdf_unlikely(QDF_STATUS_SUCCESS !=
|
||||||
qdf_nbuf_map_nbytes_single(vdev->osdev, nbuf,
|
dp_tx_msdu_single_map(vdev, tx_desc, nbuf))) {
|
||||||
QDF_DMA_TO_DEVICE, nbuf->len))) {
|
|
||||||
/* Handle failure */
|
/* Handle failure */
|
||||||
QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
|
dp_err("qdf_nbuf_map failed");
|
||||||
"qdf_nbuf_map failed");
|
|
||||||
DP_STATS_INC(vdev, tx_i.dropped.dma_error, 1);
|
DP_STATS_INC(vdev, tx_i.dropped.dma_error, 1);
|
||||||
drop_code = TX_DMA_MAP_ERR;
|
drop_code = TX_DMA_MAP_ERR;
|
||||||
goto release_desc;
|
goto release_desc;
|
||||||
@@ -2808,7 +2850,7 @@ static inline void dp_tx_comp_free_buf(struct dp_soc *soc,
|
|||||||
|
|
||||||
/* If it is TDLS mgmt, don't unmap or free the frame */
|
/* If it is TDLS mgmt, don't unmap or free the frame */
|
||||||
if (desc->flags & DP_TX_DESC_FLAG_TDLS_FRAME)
|
if (desc->flags & DP_TX_DESC_FLAG_TDLS_FRAME)
|
||||||
return dp_non_std_tx_comp_free_buff(desc, vdev);
|
return dp_non_std_tx_comp_free_buff(soc, desc, vdev);
|
||||||
|
|
||||||
/* 0 : MSDU buffer, 1 : MLE */
|
/* 0 : MSDU buffer, 1 : MLE */
|
||||||
if (desc->msdu_ext_desc) {
|
if (desc->msdu_ext_desc) {
|
||||||
|
Reference in New Issue
Block a user