qcacld-3.0: Fix double free of SKB for TSO

In case of TSO, same skb results in multiple tx_desc after
segmentation. To avoid multiple free of skb "skb->users"
count is used. Currently, race condition is observed while
incrementing "skb->users" count in ol_tx_ll_fast and before
that, tx_completion is received for all the segments which
are sent before. In this case, "skb->users" count will be 1
so, skb is removed from the skb debug node which leads double
free of skb when tx completion is received for the last segment.

To fix this, increment the "skb->users" count before sending
first segment and decrement only iwhen the last segment.

Change-Id: I26000fcda8715b737cac803ec8e64c0fb640dfe4
CRs-Fixed: 2582617
This commit is contained in:
Alok Kumar 2019-12-11 15:30:34 +05:30 • committed by nshrivas
commit 69a11f24b1

View file

@ -382,6 +382,15 @@ ol_tx_ll_fast(ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu_list)
* pointer before the ce_send call.
*/
next = qdf_nbuf_next(msdu);
/*
* Increment the skb->users count here, for this SKB, to make
* sure it will be freed only after receiving Tx completion
* of the last segment.
* Decrement skb->users count before sending last segment
*/
if (qdf_nbuf_is_tso(msdu) && segments)
qdf_nbuf_inc_users(msdu);
/* init the current segment to the 1st segment in the list */
while (segments) {
if (msdu_info.tso_info.curr_seg)
@ -451,6 +460,13 @@ ol_tx_ll_fast(ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu_list)
next_seg = NULL;
}
/* Decrement the skb-users count if segment
* is the last segment or the only segment
*/
if (tx_desc->pkt_type == OL_TX_FRM_TSO &&
segments == 0)
qdf_nbuf_tx_free(msdu, 0);
if ((ce_send_fast(pdev->ce_tx_hdl, msdu,
ep_id,
pkt_download_len) == 0)) {
@ -465,6 +481,10 @@ ol_tx_ll_fast(ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu_list)
tso_info->curr_seg = next_seg;
ol_free_remaining_tso_segs(vdev,
&msdu_info, true);
if (segments)
qdf_nbuf_tx_free(
msdu,
QDF_NBUF_PKT_ERROR);
}
/*
@ -491,10 +511,12 @@ ol_tx_ll_fast(ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu_list)
* If TSO packet, free associated
* remaining TSO segment descriptors
*/
if (qdf_nbuf_is_tso(msdu))
if (qdf_nbuf_is_tso(msdu)) {
ol_free_remaining_tso_segs(vdev,
&msdu_info, true);
qdf_nbuf_tx_free(msdu,
QDF_NBUF_PKT_ERROR);
}
TXRX_STATS_MSDU_LIST_INCR(
pdev, tx.dropped.host_reject, msdu);
/* the list of unaccepted MSDUs */