From 6d840bcb94da1f36188cf5a82b8b97671ba44f2f Mon Sep 17 00:00:00 2001 From: Venkateswara Swamy Bandaru Date: Mon, 10 Jul 2017 15:35:28 +0530 Subject: [PATCH] qcacmn: Fix for kernel panic in wifi up path Same nbuf pointer is getting used after skb_unshare. This is fixed now. Change-Id: I98cdbf5929553f20e78b04b32874beea03208cce CRs-Fixed: 2094334 --- dp/wifi3.0/dp_tx.c | 69 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 57 insertions(+), 12 deletions(-) diff --git a/dp/wifi3.0/dp_tx.c b/dp/wifi3.0/dp_tx.c index a3d17f4d9ae0..78e291c67a60 100644 --- a/dp/wifi3.0/dp_tx.c +++ b/dp/wifi3.0/dp_tx.c @@ -207,8 +207,6 @@ static uint8_t dp_tx_prepare_htt_metadata(struct dp_vdev *vdev, qdf_nbuf_t nbuf, uint8_t *hdr = NULL; - qdf_nbuf_unshare(nbuf); - HTT_TX_TCL_METADATA_VALID_HTT_SET(vdev->htt_tcl_metadata, 1); /* @@ -221,6 +219,12 @@ static uint8_t dp_tx_prepare_htt_metadata(struct dp_vdev *vdev, qdf_nbuf_t nbuf, /* Fill and add HTT metaheader */ hdr = qdf_nbuf_push_head(nbuf, htt_desc_size_aligned); + if (hdr == NULL) { + QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, + "Error in filling HTT metadata\n"); + + return 0; + } qdf_mem_copy(hdr, desc_ext, htt_desc_size); } else if (vdev->opmode == wlan_op_mode_ocb) { @@ -543,7 +547,12 @@ struct dp_tx_desc_s *dp_tx_prepare_desc_single(struct dp_vdev *vdev, * start of data through pkt_offset */ align_pad = ((unsigned long) qdf_nbuf_data(nbuf)) & 0x7; - qdf_nbuf_push_head(nbuf, align_pad); + if (qdf_nbuf_push_head(nbuf, align_pad) == NULL) { + QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, + "qdf_nbuf_push_head failed\n"); + goto failure; + } + tx_desc->pkt_offset = align_pad; /* @@ -579,6 +588,8 @@ struct dp_tx_desc_s *dp_tx_prepare_desc_single(struct dp_vdev *vdev, (vdev->opmode == wlan_op_mode_ocb))) { htt_hdr_size = dp_tx_prepare_htt_metadata(vdev, nbuf, meta_data); + if (htt_hdr_size == 0) + goto failure; tx_desc->pkt_offset += htt_hdr_size; tx_desc->flags |= DP_TX_DESC_FLAG_TO_FW; is_exception = 1; @@ -1251,16 +1262,24 @@ static qdf_nbuf_t dp_tx_prepare_sg(struct dp_vdev *vdev, qdf_nbuf_t nbuf, * @nbuf: skb * @msdu_info: MSDU info to be setup in MSDU descriptor and MSDU extension desc. * - * Return: void + * Return: NULL on failure, + * nbuf when extracted successfully */ static -void dp_tx_extract_mesh_meta_data(struct dp_vdev *vdev, qdf_nbuf_t nbuf, +qdf_nbuf_t dp_tx_extract_mesh_meta_data(struct dp_vdev *vdev, qdf_nbuf_t nbuf, struct dp_tx_msdu_info_s *msdu_info) { struct meta_hdr_s *mhdr; struct htt_tx_msdu_desc_ext2_t *meta_data = (struct htt_tx_msdu_desc_ext2_t *)&msdu_info->meta_data[0]; + nbuf = qdf_nbuf_unshare(nbuf); + if (nbuf == NULL) { + QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, + "qdf_nbuf_unshare failed\n"); + return nbuf; + } + mhdr = (struct meta_hdr_s *)qdf_nbuf_data(nbuf); qdf_mem_set(meta_data, 0, sizeof(struct htt_tx_msdu_desc_ext2_t)); @@ -1296,7 +1315,12 @@ void dp_tx_extract_mesh_meta_data(struct dp_vdev *vdev, qdf_nbuf_t nbuf, meta_data->valid_key_flags = 1; meta_data->key_flags = (mhdr->keyix & 0x3); - qdf_nbuf_pull_head(nbuf, sizeof(struct meta_hdr_s)); + if (qdf_nbuf_pull_head(nbuf, sizeof(struct meta_hdr_s)) == NULL) { + QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, + "qdf_nbuf_pull_head failed\n"); + qdf_nbuf_free(nbuf); + return NULL; + } QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG, "%s , Meta hdr %0x %0x %0x %0x %0x\n", @@ -1306,13 +1330,14 @@ void dp_tx_extract_mesh_meta_data(struct dp_vdev *vdev, qdf_nbuf_t nbuf, msdu_info->meta_data[3], msdu_info->meta_data[4]); - return; + return nbuf; } #else static -void dp_tx_extract_mesh_meta_data(struct dp_vdev *vdev, qdf_nbuf_t nbuf, +qdf_nbuf_t dp_tx_extract_mesh_meta_data(struct dp_vdev *vdev, qdf_nbuf_t nbuf, struct dp_tx_msdu_info_s *msdu_info) { + return nbuf; } #endif @@ -1379,6 +1404,8 @@ qdf_nbuf_t dp_tx_send(void *vap_dev, qdf_nbuf_t nbuf) struct dp_tx_seg_info_s seg_info; struct dp_vdev *vdev = (struct dp_vdev *) vap_dev; uint16_t peer_id = HTT_INVALID_PEER; + qdf_nbuf_t nbuf_mesh = NULL; + qdf_mem_set(&msdu_info, sizeof(msdu_info), 0x0); qdf_mem_set(&seg_info, sizeof(seg_info), 0x0); @@ -1394,8 +1421,16 @@ qdf_nbuf_t dp_tx_send(void *vap_dev, qdf_nbuf_t nbuf) msdu_info.tid = HTT_TX_EXT_TID_INVALID; DP_STATS_INC_PKT(vdev, tx_i.rcvd, 1, qdf_nbuf_len(nbuf)); - if (qdf_unlikely(vdev->mesh_vdev)) - dp_tx_extract_mesh_meta_data(vdev, nbuf, &msdu_info); + if (qdf_unlikely(vdev->mesh_vdev)) { + nbuf_mesh = dp_tx_extract_mesh_meta_data(vdev, nbuf, + &msdu_info); + if (nbuf_mesh == NULL) { + QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, + "Extracting mesh metadata failed\n"); + return nbuf; + } + nbuf = nbuf_mesh; + } /* * Get HW Queue to use for this frame. @@ -1799,9 +1834,19 @@ void dp_tx_comp_fill_tx_completion_stats(struct dp_tx_desc_s *tx_desc, qdf_nbuf_t netbuf = tx_desc->nbuf; if (!tx_desc->msdu_ext_desc) { - qdf_nbuf_pull_head(netbuf, tx_desc->pkt_offset); + if (qdf_nbuf_pull_head(netbuf, tx_desc->pkt_offset) == NULL) { + QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, + "netbuf %p offset %d\n", + netbuf, tx_desc->pkt_offset); + return; + } + } + if (qdf_nbuf_push_head(netbuf, sizeof(struct meta_hdr_s)) == NULL) { + QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, + "netbuf %p offset %d\n", netbuf, + sizeof(struct meta_hdr_s)); + return; } - qdf_nbuf_push_head(netbuf, sizeof(struct meta_hdr_s)); mhdr = (struct meta_hdr_s *)qdf_nbuf_data(netbuf); mhdr->rssi = ts->ack_frame_rssi;