qcacmn: Fix a race btw tx and tx-compl
While handling a multi-segment TSO packet, there is a race condition where, if tx complete arrives fast enough, the un-sent TSO segments may be lost forever and a previously sent segment would be attempted to be sent over. Fix the race condition. Dont use the entry after send to go to next entry. Change-Id: I023587a48bea6f3b58aac62e398cc15939bbb773 CRs-Fixed: 2174715
This commit is contained in:

committed by
nshrivas

parent
cb94e45a0e
commit
c6b4a7be50
@@ -611,29 +611,93 @@ void __printf(3, 4) qdf_snprintf(char *str_buffer, unsigned int size,
|
|||||||
#define QDF_SNPRINTF qdf_snprintf
|
#define QDF_SNPRINTF qdf_snprintf
|
||||||
|
|
||||||
#ifdef TSOSEG_DEBUG
|
#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)
|
static inline void qdf_tso_seg_dbg_bug(char *msg)
|
||||||
{
|
{
|
||||||
qdf_print(msg);
|
qdf_print(msg);
|
||||||
QDF_BUG(0);
|
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
|
static inline void
|
||||||
qdf_tso_seg_dbg_setowner(struct qdf_tso_seg_elem_t *tsoseg, void *owner)
|
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
|
static inline void
|
||||||
@@ -645,8 +709,11 @@ qdf_tso_seg_dbg_zero(struct qdf_tso_seg_elem_t *tsoseg)
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
static inline
|
static inline
|
||||||
int qdf_tso_seg_dbg_record(struct qdf_tso_seg_elem_t *tsoseg,
|
void qdf_tso_seg_dbg_init(struct qdf_tso_seg_elem_t *tsoseg)
|
||||||
uint16_t caller)
|
{
|
||||||
|
};
|
||||||
|
static inline
|
||||||
|
int qdf_tso_seg_dbg_record(struct qdf_tso_seg_elem_t *tsoseg, short id)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
@@ -43,6 +43,9 @@
|
|||||||
/* Include Files */
|
/* Include Files */
|
||||||
#include <i_qdf_types.h>
|
#include <i_qdf_types.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#ifdef TSOSEG_DEBUG
|
||||||
|
#include <qdf_atomic.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Preprocessor definitions and constants */
|
/* Preprocessor definitions and constants */
|
||||||
#define QDF_MAX_SGLIST 4
|
#define QDF_MAX_SGLIST 4
|
||||||
@@ -876,7 +879,7 @@ struct qdf_tso_frag_t {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#define FRAG_NUM_MAX 6
|
#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
|
* struct qdf_tso_flags_t - TSO specific flags
|
||||||
@@ -956,20 +959,33 @@ enum tsoseg_dbg_caller_e {
|
|||||||
TSOSEG_LOC_UNDEFINED,
|
TSOSEG_LOC_UNDEFINED,
|
||||||
TSOSEG_LOC_INIT1,
|
TSOSEG_LOC_INIT1,
|
||||||
TSOSEG_LOC_INIT2,
|
TSOSEG_LOC_INIT2,
|
||||||
|
TSOSEG_LOC_FREE,
|
||||||
|
TSOSEG_LOC_ALLOC,
|
||||||
TSOSEG_LOC_DEINIT,
|
TSOSEG_LOC_DEINIT,
|
||||||
|
TSOSEG_LOC_GETINFO,
|
||||||
|
TSOSEG_LOC_FILLHTTSEG,
|
||||||
|
TSOSEG_LOC_FILLCMNSEG,
|
||||||
TSOSEG_LOC_PREPARETSO,
|
TSOSEG_LOC_PREPARETSO,
|
||||||
TSOSEG_LOC_TXPREPLLFAST,
|
TSOSEG_LOC_TXPREPLLFAST,
|
||||||
TSOSEG_LOC_UNMAPTSO,
|
TSOSEG_LOC_UNMAPTSO,
|
||||||
TSOSEG_LOC_ALLOC,
|
TSOSEG_LOC_UNMAPLAST,
|
||||||
TSOSEG_LOC_FREE,
|
TSOSEG_LOC_FORCE_FREE,
|
||||||
};
|
};
|
||||||
#ifdef TSOSEG_DEBUG
|
#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
|
#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 {
|
struct qdf_tso_seg_dbg_t {
|
||||||
void *txdesc; /* owner - (ol_txrx_tx_desc_t *) */
|
void *txdesc; /* owner - (ol_txrx_tx_desc_t *) */
|
||||||
int cur; /* index of last valid entry */
|
qdf_atomic_t cur; /* index of last valid entry */
|
||||||
uint16_t history[MAX_TSO_SEG_ACT_HISTORY];
|
struct qdf_tso_seg_dbg_history_t h[MAX_TSO_SEG_ACT_HISTORY];
|
||||||
};
|
};
|
||||||
#endif /* TSOSEG_DEBUG */
|
#endif /* TSOSEG_DEBUG */
|
||||||
|
|
||||||
@@ -980,8 +996,10 @@ struct qdf_tso_seg_dbg_t {
|
|||||||
*/
|
*/
|
||||||
struct qdf_tso_seg_elem_t {
|
struct qdf_tso_seg_elem_t {
|
||||||
struct qdf_tso_seg_t seg;
|
struct qdf_tso_seg_t seg;
|
||||||
uint16_t cookie:15,
|
uint32_t cookie:13,
|
||||||
on_freelist:1;
|
on_freelist:1,
|
||||||
|
sent_to_target:1,
|
||||||
|
force_free:1;
|
||||||
struct qdf_tso_seg_elem_t *next;
|
struct qdf_tso_seg_elem_t *next;
|
||||||
#ifdef TSOSEG_DEBUG
|
#ifdef TSOSEG_DEBUG
|
||||||
struct qdf_tso_seg_dbg_t dbg;
|
struct qdf_tso_seg_dbg_t dbg;
|
||||||
|
@@ -2836,6 +2836,7 @@ static inline void __qdf_nbuf_fill_tso_cmn_seg_info(
|
|||||||
tso_cmn_info->eit_hdr_len,
|
tso_cmn_info->eit_hdr_len,
|
||||||
curr_seg->seg.tso_flags.tcp_seq_num,
|
curr_seg->seg.tso_flags.tcp_seq_num,
|
||||||
curr_seg->seg.total_len);
|
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);
|
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__,
|
TSO_DEBUG("%s[%d] skb frag len %d tso frag len %d\n", __func__,
|
||||||
__LINE__, skb_frag_len, tso_frag_len);
|
__LINE__, skb_frag_len, tso_frag_len);
|
||||||
num_seg = tso_info->num_segs;
|
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);
|
DMA_TO_DEVICE);
|
||||||
if (unlikely(dma_mapping_error(osdev->dev,
|
if (unlikely(dma_mapping_error(osdev->dev,
|
||||||
tso_frag_paddr))) {
|
tso_frag_paddr))) {
|
||||||
qdf_print("DMA mapping error!\n");
|
qdf_print("%s:%d DMA mapping error!\n",
|
||||||
|
__func__, __LINE__);
|
||||||
qdf_assert(0);
|
qdf_assert(0);
|
||||||
return 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)
|
if (!num_seg)
|
||||||
curr_seg->seg.tso_flags.fin = tso_cmn_info.tcphdr->fin;
|
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;
|
curr_seg = curr_seg->next;
|
||||||
}
|
}
|
||||||
return tso_info->num_segs;
|
return tso_info->num_segs;
|
||||||
@@ -3076,13 +3085,13 @@ void __qdf_nbuf_unmap_tso_segment(qdf_device_t osdev,
|
|||||||
qdf_assert(0);
|
qdf_assert(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
qdf_tso_seg_dbg_record(tso_seg, TSOSEG_LOC_UNMAPTSO);
|
|
||||||
dma_unmap_single(osdev->dev,
|
dma_unmap_single(osdev->dev,
|
||||||
tso_seg->seg.tso_frags[num_frags].paddr,
|
tso_seg->seg.tso_frags[num_frags].paddr,
|
||||||
tso_seg->seg.tso_frags[num_frags].length,
|
tso_seg->seg.tso_frags[num_frags].length,
|
||||||
__qdf_dma_dir_to_os(QDF_DMA_TO_DEVICE));
|
__qdf_dma_dir_to_os(QDF_DMA_TO_DEVICE));
|
||||||
tso_seg->seg.tso_frags[num_frags].paddr = 0;
|
tso_seg->seg.tso_frags[num_frags].paddr = 0;
|
||||||
num_frags--;
|
num_frags--;
|
||||||
|
qdf_tso_seg_dbg_record(tso_seg, TSOSEG_LOC_UNMAPTSO);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_last_seg) {
|
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,
|
tso_seg->seg.tso_frags[0].length,
|
||||||
__qdf_dma_dir_to_os(QDF_DMA_TO_DEVICE));
|
__qdf_dma_dir_to_os(QDF_DMA_TO_DEVICE));
|
||||||
tso_seg->seg.tso_frags[0].paddr = 0;
|
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);
|
qdf_export_symbol(__qdf_nbuf_unmap_tso_segment);
|
||||||
|
Reference in New Issue
Block a user