diff --git a/qdf/inc/qdf_trace.h b/qdf/inc/qdf_trace.h index e0c53b9bd5..9bf4242aa0 100644 --- a/qdf/inc/qdf_trace.h +++ b/qdf/inc/qdf_trace.h @@ -611,29 +611,93 @@ void __printf(3, 4) qdf_snprintf(char *str_buffer, unsigned int size, #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); }; +/** + * qdf_tso_seg_dbg_init - initialize TSO segment debug structure + * @tsoseg : structure to initialize + * + * TSO segment dbg structures are attached to qdf_tso_seg_elem_t + * structures and are allocated only of TSOSEG_DEBUG is defined. + * When allocated, at the time of the tso_seg_pool initialization, + * which goes with tx_desc initialization (1:1), each structure holds + * a number of (currently 16) history entries, basically describing + * what operation has been performed on this particular tso_seg_elem. + * This history buffer is a circular buffer and the current index is + * held in an atomic variable called cur. It is incremented every + * operation. Each of these operations are added with the function + * qdf_tso_seg_dbg_record. + * For each segment, this initialization function MUST be called PRIOR + * TO any _dbg_record() function calls. + * On free, qdf_tso_seg_elem structure is cleared (using qdf_tso_seg_dbg_zero) + * which clears the tso_desc, BUT DOES NOT CLEAR THE HISTORY element. + * + * Return: + * None + */ +static inline +void qdf_tso_seg_dbg_init(struct qdf_tso_seg_elem_t *tsoseg) +{ + tsoseg->dbg.txdesc = NULL; + qdf_atomic_init(&tsoseg->dbg.cur); /* history empty */ +} + +/** + * qdf_tso_seg_dbg_record - add a history entry to TSO debug structure + * @tsoseg : structure to initialize + * @id : operation ID (identifies the caller) + * + * Adds a history entry to the history circular buffer. Each entry + * contains an operation id (caller, as currently each ID is used only + * once in the source, so it directly identifies the src line that invoked + * the recording. + * + * qdf_tso_seg_dbg_record CAN ONLY BE CALLED AFTER the entry is initialized + * by qdf_tso_seg_dbg_init. + * + * The entry to be added is written at the location pointed by the atomic + * variable called cur. Cur is an ever increasing atomic variable. It is + * masked so that only the lower 4 bits are used (16 history entries). + * + * Return: + * int: the entry this record was recorded at + */ +static inline +int qdf_tso_seg_dbg_record(struct qdf_tso_seg_elem_t *tsoseg, short id) +{ + int rc = -1; + unsigned int c; + + qdf_assert(tsoseg); + + if (id == TSOSEG_LOC_ALLOC) { + c = qdf_atomic_read(&tsoseg->dbg.cur); + /* dont crash on the very first alloc on the segment */ + c &= 0x0f; + /* allow only INIT and FREE ops before ALLOC */ + if (tsoseg->dbg.h[c].id >= id) + qdf_tso_seg_dbg_bug("Rogue TSO seg alloc"); + } + c = qdf_atomic_inc_return(&tsoseg->dbg.cur); + + c &= 0x0f; + tsoseg->dbg.h[c].ts = qdf_get_log_timestamp(); + tsoseg->dbg.h[c].id = id; + rc = c; + + return rc; +}; + static inline void qdf_tso_seg_dbg_setowner(struct qdf_tso_seg_elem_t *tsoseg, void *owner) { - tsoseg->dbg.txdesc = owner; + if (tsoseg) + tsoseg->dbg.txdesc = owner; }; static inline void @@ -645,8 +709,11 @@ qdf_tso_seg_dbg_zero(struct qdf_tso_seg_elem_t *tsoseg) #else static inline -int qdf_tso_seg_dbg_record(struct qdf_tso_seg_elem_t *tsoseg, - uint16_t caller) +void qdf_tso_seg_dbg_init(struct qdf_tso_seg_elem_t *tsoseg) +{ +}; +static inline +int qdf_tso_seg_dbg_record(struct qdf_tso_seg_elem_t *tsoseg, short id) { return 0; }; diff --git a/qdf/inc/qdf_types.h b/qdf/inc/qdf_types.h index d48763da74..acf65caddc 100644 --- a/qdf/inc/qdf_types.h +++ b/qdf/inc/qdf_types.h @@ -43,6 +43,9 @@ /* Include Files */ #include #include +#ifdef TSOSEG_DEBUG +#include +#endif /* Preprocessor definitions and constants */ #define QDF_MAX_SGLIST 4 @@ -876,7 +879,7 @@ struct qdf_tso_frag_t { }; #define FRAG_NUM_MAX 6 -#define TSO_SEG_MAGIC_COOKIE 0x7EED +#define TSO_SEG_MAGIC_COOKIE 0x1EED /** * struct qdf_tso_flags_t - TSO specific flags @@ -956,20 +959,33 @@ enum tsoseg_dbg_caller_e { TSOSEG_LOC_UNDEFINED, TSOSEG_LOC_INIT1, TSOSEG_LOC_INIT2, + TSOSEG_LOC_FREE, + TSOSEG_LOC_ALLOC, TSOSEG_LOC_DEINIT, + TSOSEG_LOC_GETINFO, + TSOSEG_LOC_FILLHTTSEG, + TSOSEG_LOC_FILLCMNSEG, TSOSEG_LOC_PREPARETSO, TSOSEG_LOC_TXPREPLLFAST, TSOSEG_LOC_UNMAPTSO, - TSOSEG_LOC_ALLOC, - TSOSEG_LOC_FREE, + TSOSEG_LOC_UNMAPLAST, + TSOSEG_LOC_FORCE_FREE, }; #ifdef TSOSEG_DEBUG +/** + * WARNING: Don't change the history size without changing the wrap + * code in qdf_tso_seg_dbg_record function + */ #define MAX_TSO_SEG_ACT_HISTORY 16 +struct qdf_tso_seg_dbg_history_t { + uint64_t ts; + short id; +}; 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]; + qdf_atomic_t cur; /* index of last valid entry */ + struct qdf_tso_seg_dbg_history_t h[MAX_TSO_SEG_ACT_HISTORY]; }; #endif /* TSOSEG_DEBUG */ @@ -980,8 +996,10 @@ struct qdf_tso_seg_dbg_t { */ struct qdf_tso_seg_elem_t { struct qdf_tso_seg_t seg; - uint16_t cookie:15, - on_freelist:1; + uint32_t cookie:13, + on_freelist:1, + sent_to_target:1, + force_free:1; struct qdf_tso_seg_elem_t *next; #ifdef TSOSEG_DEBUG struct qdf_tso_seg_dbg_t dbg; diff --git a/qdf/linux/src/qdf_nbuf.c b/qdf/linux/src/qdf_nbuf.c index 7a0d4d4557..7e3534a8ee 100644 --- a/qdf/linux/src/qdf_nbuf.c +++ b/qdf/linux/src/qdf_nbuf.c @@ -2836,6 +2836,7 @@ static inline void __qdf_nbuf_fill_tso_cmn_seg_info( tso_cmn_info->eit_hdr_len, curr_seg->seg.tso_flags.tcp_seq_num, curr_seg->seg.total_len); + qdf_tso_seg_dbg_record(curr_seg, TSOSEG_LOC_FILLCMNSEG); } /** @@ -2898,6 +2899,12 @@ uint32_t __qdf_nbuf_get_tso_info(qdf_device_t osdev, struct sk_buff *skb, tso_frag_vaddr, tso_frag_len, DMA_TO_DEVICE); } + if (unlikely(dma_mapping_error(osdev->dev, + tso_frag_paddr))) { + qdf_print("%s:%d DMA mapping error!\n", __func__, __LINE__); + qdf_assert(0); + return 0; + } TSO_DEBUG("%s[%d] skb frag len %d tso frag len %d\n", __func__, __LINE__, skb_frag_len, tso_frag_len); num_seg = tso_info->num_segs; @@ -3024,7 +3031,8 @@ uint32_t __qdf_nbuf_get_tso_info(qdf_device_t osdev, struct sk_buff *skb, DMA_TO_DEVICE); if (unlikely(dma_mapping_error(osdev->dev, tso_frag_paddr))) { - qdf_print("DMA mapping error!\n"); + qdf_print("%s:%d DMA mapping error!\n", + __func__, __LINE__); qdf_assert(0); return 0; } @@ -3036,6 +3044,7 @@ uint32_t __qdf_nbuf_get_tso_info(qdf_device_t osdev, struct sk_buff *skb, if (!num_seg) curr_seg->seg.tso_flags.fin = tso_cmn_info.tcphdr->fin; + qdf_tso_seg_dbg_record(curr_seg, TSOSEG_LOC_GETINFO); curr_seg = curr_seg->next; } return tso_info->num_segs; @@ -3076,13 +3085,13 @@ 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, __qdf_dma_dir_to_os(QDF_DMA_TO_DEVICE)); tso_seg->seg.tso_frags[num_frags].paddr = 0; num_frags--; + qdf_tso_seg_dbg_record(tso_seg, TSOSEG_LOC_UNMAPTSO); } if (is_last_seg) { @@ -3097,6 +3106,7 @@ void __qdf_nbuf_unmap_tso_segment(qdf_device_t osdev, tso_seg->seg.tso_frags[0].length, __qdf_dma_dir_to_os(QDF_DMA_TO_DEVICE)); tso_seg->seg.tso_frags[0].paddr = 0; + qdf_tso_seg_dbg_record(tso_seg, TSOSEG_LOC_UNMAPLAST); } } qdf_export_symbol(__qdf_nbuf_unmap_tso_segment);