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
这个提交包含在:
Jinwei Chen
2020-04-23 20:25:18 +08:00
提交者 nshrivas
父节点 9aa9221c98
当前提交 446ac2d46e

查看文件

@@ -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
* @soc: dp_soc handle
* @tx_desc: TX descriptor
* @vdev: datapath vdev handle
*
* 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 hal_tx_completion_status ts = {0};
qdf_nbuf_t nbuf = tx_desc->nbuf;
if (qdf_unlikely(!vdev)) {
dp_err("vdev is null!");
return;
dp_err_rl("vdev is null!");
goto error;
}
hal_tx_comp_get_status(&tx_desc->comp, &ts, vdev->pdev->soc->hal_soc);
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.ctxt,
nbuf, ts.status);
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
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)
{
}
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
/**
@@ -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);
if (qdf_unlikely(QDF_STATUS_SUCCESS !=
qdf_nbuf_map_nbytes_single(vdev->osdev, nbuf,
QDF_DMA_TO_DEVICE, nbuf->len))) {
dp_tx_msdu_single_map(vdev, tx_desc, nbuf))) {
/* Handle failure */
QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
"qdf_nbuf_map failed");
dp_err("qdf_nbuf_map failed");
DP_STATS_INC(vdev, tx_i.dropped.dma_error, 1);
drop_code = TX_DMA_MAP_ERR;
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 (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 */
if (desc->msdu_ext_desc) {