From c59be52d4725bf8d95bed46907b515b3bed88074 Mon Sep 17 00:00:00 2001 From: Orhan K AKYILDIZ Date: Wed, 19 Apr 2017 21:21:45 -0700 Subject: [PATCH] qcacmn: Add debug for TSO seg double-free Add TSO segment debug code, in a featurized way, so that when disabled, it will tolerate redundant returns to the internal pool and when enabled, it will check sanity on free, and record a history of last 16 actions on the segment in the object. Goes with the corresponding cld3 component counterpart. Change-Id: Ifee2ceae940043031e4861d0e4f5d19a51262229 CRs-Fixed: 2036665 --- qdf/inc/qdf_trace.h | 56 ++++++++++++++++++++++++++++++++++++++++ qdf/inc/qdf_types.h | 30 +++++++++++++++++++++ qdf/linux/src/qdf_nbuf.c | 1 + 3 files changed, 87 insertions(+) diff --git a/qdf/inc/qdf_trace.h b/qdf/inc/qdf_trace.h index 454f385e73..ae6ba2c311 100644 --- a/qdf/inc/qdf_trace.h +++ b/qdf/inc/qdf_trace.h @@ -476,6 +476,62 @@ void __printf(3, 4) qdf_snprintf(char *str_buffer, unsigned int size, char *str_format, ...); #define QDF_SNPRINTF qdf_snprintf + +#ifdef TSOSEG_DEBUG +static inline +int qdf_tso_seg_dbg_record(struct qdf_tso_seg_elem_t *tsoseg, + uint16_t caller) +{ + int rc = -1; + + if (tsoseg != NULL) { + tsoseg->dbg.cur++; tsoseg->dbg.cur &= 0x0f; + tsoseg->dbg.history[tsoseg->dbg.cur] = caller; + rc = tsoseg->dbg.cur; + } + return rc; +}; +static inline void qdf_tso_seg_dbg_bug(char *msg) +{ + qdf_print(msg); + QDF_BUG(0); +}; + +static inline void +qdf_tso_seg_dbg_setowner(struct qdf_tso_seg_elem_t *tsoseg, void *owner) +{ + tsoseg->dbg.txdesc = owner; +}; + +static inline void +qdf_tso_seg_dbg_zero(struct qdf_tso_seg_elem_t *tsoseg) +{ + memset(tsoseg, 0, offsetof(struct qdf_tso_seg_elem_t, dbg)); + return; +}; + +#else +static inline +int qdf_tso_seg_dbg_record(struct qdf_tso_seg_elem_t *tsoseg, + uint16_t caller) +{ + return 0; +}; +static inline void qdf_tso_seg_dbg_bug(char *msg) +{ +}; +static inline void +qdf_tso_seg_dbg_setowner(struct qdf_tso_seg_elem_t *tsoseg, void *owner) +{ +}; +static inline int +qdf_tso_seg_dbg_zero(struct qdf_tso_seg_elem_t *tsoseg) +{ + memset(tsoseg, 0, sizeof(struct qdf_tso_seg_elem_t)); + return 0; +}; + +#endif /* TSOSEG_DEBUG */ #else #define DPTRACE(x) diff --git a/qdf/inc/qdf_types.h b/qdf/inc/qdf_types.h index 10304506de..d7545b113e 100644 --- a/qdf/inc/qdf_types.h +++ b/qdf/inc/qdf_types.h @@ -690,6 +690,33 @@ struct qdf_tso_seg_t { struct qdf_tso_frag_t tso_frags[FRAG_NUM_MAX]; }; +/** + * TSO seg elem action caller locations: goes into dbg.history below. + * Needed to be defined outside of the feature so that + * callers can be coded without ifdefs (even if they get + * resolved to nothing) + */ +enum tsoseg_dbg_caller_e { + TSOSEG_LOC_UNDEFINED, + TSOSEG_LOC_INIT1, + TSOSEG_LOC_INIT2, + TSOSEG_LOC_DEINIT, + TSOSEG_LOC_PREPARETSO, + TSOSEG_LOC_TXPREPLLFAST, + TSOSEG_LOC_UNMAPTSO, + TSOSEG_LOC_ALLOC, + TSOSEG_LOC_FREE, +}; +#ifdef TSOSEG_DEBUG + +#define MAX_TSO_SEG_ACT_HISTORY 16 +struct qdf_tso_seg_dbg_t { + void *txdesc; /* owner - (ol_txrx_tx_desc_t *) */ + int cur; /* index of last valid entry */ + uint16_t history[MAX_TSO_SEG_ACT_HISTORY]; +}; +#endif /* TSOSEG_DEBUG */ + /** * qdf_tso_seg_elem_t - tso segment element * @seg: instance of segment @@ -700,6 +727,9 @@ struct qdf_tso_seg_elem_t { uint16_t cookie:15, on_freelist:1; struct qdf_tso_seg_elem_t *next; +#ifdef TSOSEG_DEBUG + struct qdf_tso_seg_dbg_t dbg; +#endif /* TSOSEG_DEBUG */ }; /** diff --git a/qdf/linux/src/qdf_nbuf.c b/qdf/linux/src/qdf_nbuf.c index 9ed11cbc4d..0a16028072 100644 --- a/qdf/linux/src/qdf_nbuf.c +++ b/qdf/linux/src/qdf_nbuf.c @@ -1982,6 +1982,7 @@ void __qdf_nbuf_unmap_tso_segment(qdf_device_t osdev, qdf_assert(0); return; } + qdf_tso_seg_dbg_record(tso_seg, TSOSEG_LOC_UNMAPTSO); dma_unmap_single(osdev->dev, tso_seg->seg.tso_frags[num_frags].paddr, tso_seg->seg.tso_frags[num_frags].length,