summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/dp/txrx/ol_txrx.c229
-rw-r--r--core/dp/txrx/ol_txrx_types.h25
2 files changed, 196 insertions, 58 deletions
diff --git a/core/dp/txrx/ol_txrx.c b/core/dp/txrx/ol_txrx.c
index 2b8ce31091e4..a8d38f002d3a 100644
--- a/core/dp/txrx/ol_txrx.c
+++ b/core/dp/txrx/ol_txrx.c
@@ -89,6 +89,9 @@
#include <htt_internal.h>
+/* thresh for peer's cached buf queue beyond which the elements are dropped */
+#define OL_TXRX_CACHED_BUFQ_THRESH 128
+
#if defined(CONFIG_HL_SUPPORT) && defined(FEATURE_WLAN_TDLS)
/**
@@ -2279,6 +2282,7 @@ ol_txrx_vdev_detach(ol_txrx_vdev_handle vdev,
void ol_txrx_flush_rx_frames(struct ol_txrx_peer_t *peer,
bool drop)
{
+ struct ol_txrx_cached_bufq_t *bufqi;
struct ol_rx_cached_buf *cache_buf;
QDF_STATUS ret;
ol_txrx_rx_fp data_rx = NULL;
@@ -2289,8 +2293,8 @@ void ol_txrx_flush_rx_frames(struct ol_txrx_peer_t *peer,
}
qdf_assert(peer->vdev);
-
qdf_spin_lock_bh(&peer->peer_info_lock);
+ bufqi = &peer->bufq_info;
if (peer->state >= OL_TXRX_PEER_STATE_CONN && peer->vdev->rx)
data_rx = peer->vdev->rx;
@@ -2298,12 +2302,14 @@ void ol_txrx_flush_rx_frames(struct ol_txrx_peer_t *peer,
drop = true;
qdf_spin_unlock_bh(&peer->peer_info_lock);
- qdf_spin_lock_bh(&peer->bufq_lock);
- cache_buf = list_entry((&peer->cached_bufq)->next,
+ qdf_spin_lock_bh(&bufqi->bufq_lock);
+ cache_buf = list_entry((&bufqi->cached_bufq)->next,
typeof(*cache_buf), list);
- while (!list_empty(&peer->cached_bufq)) {
+ while (!list_empty(&bufqi->cached_bufq)) {
list_del(&cache_buf->list);
- qdf_spin_unlock_bh(&peer->bufq_lock);
+ bufqi->curr--;
+ qdf_assert(bufqi->curr >= 0);
+ qdf_spin_unlock_bh(&bufqi->bufq_lock);
if (drop) {
qdf_nbuf_free(cache_buf->buf);
} else {
@@ -2313,11 +2319,12 @@ void ol_txrx_flush_rx_frames(struct ol_txrx_peer_t *peer,
qdf_nbuf_free(cache_buf->buf);
}
qdf_mem_free(cache_buf);
- qdf_spin_lock_bh(&peer->bufq_lock);
- cache_buf = list_entry((&peer->cached_bufq)->next,
+ qdf_spin_lock_bh(&bufqi->bufq_lock);
+ cache_buf = list_entry((&bufqi->cached_bufq)->next,
typeof(*cache_buf), list);
}
- qdf_spin_unlock_bh(&peer->bufq_lock);
+ bufqi->qdepth_no_thresh = bufqi->curr;
+ qdf_spin_unlock_bh(&bufqi->bufq_lock);
qdf_atomic_dec(&peer->flush_in_progress);
}
@@ -2430,7 +2437,7 @@ ol_txrx_peer_attach(ol_txrx_vdev_handle vdev, uint8_t *peer_mac_addr)
ol_txrx_peer_txqs_init(pdev, peer);
- INIT_LIST_HEAD(&peer->cached_bufq);
+ INIT_LIST_HEAD(&peer->bufq_info.cached_bufq);
qdf_spin_lock_bh(&pdev->peer_ref_mutex);
/* add this peer into the vdev's list */
TAILQ_INSERT_TAIL(&vdev->peer_list, peer, peer_list_elem);
@@ -2448,7 +2455,9 @@ ol_txrx_peer_attach(ol_txrx_vdev_handle vdev, uint8_t *peer_mac_addr)
peer->peer_ids[i] = HTT_INVALID_PEER;
qdf_spinlock_create(&peer->peer_info_lock);
- qdf_spinlock_create(&peer->bufq_lock);
+ qdf_spinlock_create(&peer->bufq_info.bufq_lock);
+
+ peer->bufq_info.thresh = OL_TXRX_CACHED_BUFQ_THRESH;
qdf_atomic_init(&peer->delete_in_progress);
qdf_atomic_init(&peer->flush_in_progress);
@@ -3336,7 +3345,7 @@ void ol_txrx_peer_detach(ol_txrx_peer_handle peer)
htt_rx_reorder_log_print(peer->vdev->pdev->htt_pdev);
qdf_spinlock_destroy(&peer->peer_info_lock);
- qdf_spinlock_destroy(&peer->bufq_lock);
+ qdf_spinlock_destroy(&peer->bufq_info.bufq_lock);
/*
* set delete_in_progress to identify that wma
* is waiting for unmap massage for this peer
@@ -3959,6 +3968,61 @@ ol_txrx_stats(uint8_t vdev_id, char *buffer, unsigned buf_len)
return len;
}
+#ifdef QCA_SUPPORT_TXRX_LOCAL_PEER_ID
+/**
+ * ol_txrx_disp_peer_cached_bufq_stats() - display peer cached_bufq stats
+ * @peer: peer pointer
+ *
+ * Return: None
+ */
+static void ol_txrx_disp_peer_cached_bufq_stats(struct ol_txrx_peer_t *peer)
+{
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
+ "cached_bufq: curr %d drops %d hwm %d whatifs %d thresh %d",
+ peer->bufq_info.curr,
+ peer->bufq_info.dropped,
+ peer->bufq_info.high_water_mark,
+ peer->bufq_info.qdepth_no_thresh,
+ peer->bufq_info.thresh);
+}
+
+/**
+ * ol_txrx_disp_peer_stats() - display peer stats
+ * @pdev: pdev pointer
+ *
+ * Return: None
+ */
+static void ol_txrx_disp_peer_stats(ol_txrx_pdev_handle pdev)
+{ int i;
+ struct ol_txrx_peer_t *peer;
+ struct hif_opaque_softc *osc = cds_get_context(QDF_MODULE_ID_HIF);
+
+ if (osc && hif_is_load_or_unload_in_progress(HIF_GET_SOFTC(osc)))
+ return;
+
+ for (i = 0; i < OL_TXRX_NUM_LOCAL_PEER_IDS; i++) {
+ qdf_spin_lock_bh(&pdev->local_peer_ids.lock);
+ peer = pdev->local_peer_ids.map[i];
+ if (peer)
+ OL_TXRX_PEER_INC_REF_CNT(peer);
+ qdf_spin_unlock_bh(&pdev->local_peer_ids.lock);
+
+ if (peer) {
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
+ "stats: peer 0x%p local peer id %d", peer, i);
+ ol_txrx_disp_peer_cached_bufq_stats(peer);
+ OL_TXRX_PEER_UNREF_DELETE(peer);
+ }
+ }
+}
+#else
+static void ol_txrx_disp_peer_stats(ol_txrx_pdev_handle pdev)
+{
+ QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
+ "peer stats not supported w/o QCA_SUPPORT_TXRX_LOCAL_PEER_ID");
+}
+#endif
+
void ol_txrx_stats_display(ol_txrx_pdev_handle pdev)
{
QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
@@ -4050,6 +4114,8 @@ void ol_txrx_stats_display(ol_txrx_pdev_handle pdev)
pdev->stats.pub.rx.rx_ind_histogram.pkts_41_50,
pdev->stats.pub.rx.rx_ind_histogram.pkts_51_60,
pdev->stats.pub.rx.rx_ind_histogram.pkts_61_plus);
+
+ ol_txrx_disp_peer_stats(pdev);
}
void ol_txrx_stats_clear(ol_txrx_pdev_handle pdev)
@@ -4541,6 +4607,31 @@ QDF_STATUS ol_txrx_clear_stats(uint16_t value)
}
/**
+ * ol_txrx_drop_nbuf_list() - drop an nbuf list
+ * @buf_list: buffer list to be dropepd
+ *
+ * Return: int (number of bufs dropped)
+ */
+static inline int ol_txrx_drop_nbuf_list(qdf_nbuf_t buf_list)
+{
+ int num_dropped = 0;
+ qdf_nbuf_t buf, next_buf;
+ ol_txrx_pdev_handle pdev = cds_get_context(QDF_MODULE_ID_TXRX);
+
+ buf = buf_list;
+ while (buf) {
+ next_buf = qdf_nbuf_queue_next(buf);
+ if (pdev)
+ TXRX_STATS_MSDU_INCR(pdev,
+ rx.dropped_peer_invalid, buf);
+ qdf_nbuf_free(buf);
+ buf = next_buf;
+ num_dropped++;
+ }
+ return num_dropped;
+}
+
+/**
* ol_rx_data_cb() - data rx callback
* @peer: peer
* @buf_list: buffer list
@@ -4553,6 +4644,7 @@ static void ol_rx_data_cb(struct ol_txrx_pdev_t *pdev,
{
void *cds_ctx = cds_get_global_context();
void *osif_dev;
+ uint8_t drop_count = 0;
qdf_nbuf_t buf, next_buf;
QDF_STATUS ret;
ol_txrx_rx_fp data_rx = NULL;
@@ -4579,13 +4671,13 @@ static void ol_rx_data_cb(struct ol_txrx_pdev_t *pdev,
osif_dev = peer->vdev->osif_dev;
qdf_spin_unlock_bh(&peer->peer_info_lock);
- qdf_spin_lock_bh(&peer->bufq_lock);
- if (!list_empty(&peer->cached_bufq)) {
- qdf_spin_unlock_bh(&peer->bufq_lock);
+ qdf_spin_lock_bh(&peer->bufq_info.bufq_lock);
+ if (!list_empty(&peer->bufq_info.cached_bufq)) {
+ qdf_spin_unlock_bh(&peer->bufq_info.bufq_lock);
/* Flush the cached frames to HDD before passing new rx frame */
ol_txrx_flush_rx_frames(peer, 0);
} else
- qdf_spin_unlock_bh(&peer->bufq_lock);
+ qdf_spin_unlock_bh(&peer->bufq_info.bufq_lock);
buf = buf_list;
while (buf) {
@@ -4603,18 +4695,71 @@ static void ol_rx_data_cb(struct ol_txrx_pdev_t *pdev,
return;
free_buf:
- ol_txrx_warn("%s:Dropping frames", __func__);
- buf = buf_list;
+ drop_count = ol_txrx_drop_nbuf_list(buf_list);
+ ol_txrx_warn("%s:Dropped frames %u", __func__, drop_count);
+}
+
+/* print for every 16th packet */
+#define OL_TXRX_PRINT_RATE_LIMIT_THRESH 0x0f
+
+static QDF_STATUS ol_txrx_enqueue_rx_frames(
+ struct ol_txrx_cached_bufq_t *bufqi,
+ qdf_nbuf_t rx_buf_list)
+{
+ struct ol_rx_cached_buf *cache_buf;
+ qdf_nbuf_t buf, next_buf;
+ QDF_STATUS status = QDF_STATUS_SUCCESS;
+ int dropped = 0;
+ static uint32_t count;
+ bool thresh_crossed = false;
+
+
+ if ((count++ & OL_TXRX_PRINT_RATE_LIMIT_THRESH) == 0)
+ ol_txrx_info(
+ "Data on the peer before it is registered bufq->curr %d bufq->drops %d",
+ bufqi->curr, bufqi->dropped);
+
+ qdf_spin_lock_bh(&bufqi->bufq_lock);
+ if (bufqi->curr >= bufqi->thresh) {
+ status = QDF_STATUS_E_FAULT;
+ dropped = ol_txrx_drop_nbuf_list(rx_buf_list);
+ bufqi->dropped += dropped;
+ bufqi->qdepth_no_thresh += dropped;
+
+ if (bufqi->qdepth_no_thresh > bufqi->high_water_mark)
+ bufqi->high_water_mark = bufqi->qdepth_no_thresh;
+
+ thresh_crossed = true;
+ }
+
+ qdf_spin_unlock_bh(&bufqi->bufq_lock);
+
+ if (thresh_crossed)
+ goto end;
+
+ buf = rx_buf_list;
while (buf) {
next_buf = qdf_nbuf_queue_next(buf);
- if (pdev)
- TXRX_STATS_MSDU_INCR(pdev,
- rx.dropped_peer_invalid, buf);
- qdf_nbuf_free(buf);
+ cache_buf = qdf_mem_malloc(sizeof(*cache_buf));
+ if (!cache_buf) {
+ ol_txrx_err(
+ "Failed to allocate buf to cache the rx frames");
+ qdf_nbuf_free(buf);
+ } else {
+ /* Add NULL terminator */
+ qdf_nbuf_set_next(buf, NULL);
+ cache_buf->buf = buf;
+ qdf_spin_lock_bh(&bufqi->bufq_lock);
+ list_add_tail(&cache_buf->list,
+ &bufqi->cached_bufq);
+ bufqi->curr++;
+ qdf_spin_unlock_bh(&bufqi->bufq_lock);
+ }
buf = next_buf;
}
+end:
+ return status;
}
-
/**
* ol_rx_data_process() - process rx frame
* @peer: peer
@@ -4628,9 +4773,9 @@ void ol_rx_data_process(struct ol_txrx_peer_t *peer,
/* Firmware data path active response will use shim RX thread
* T2H MSG running on SIRQ context,
* IPA kernel module API should not be called on SIRQ CTXT */
- qdf_nbuf_t buf, next_buf;
ol_txrx_rx_fp data_rx = NULL;
ol_txrx_pdev_handle pdev = cds_get_context(QDF_MODULE_ID_TXRX);
+ uint8_t drop_count;
if ((!peer) || (!pdev)) {
ol_txrx_err("peer/pdev is NULL");
@@ -4650,29 +4795,9 @@ void ol_rx_data_process(struct ol_txrx_peer_t *peer,
* which will be flushed to HDD once that station is registered.
*/
if (!data_rx) {
- struct ol_rx_cached_buf *cache_buf;
-
- ol_txrx_info(
- "Data on the peer before it is registered!!!");
- buf = rx_buf_list;
- while (buf) {
- next_buf = qdf_nbuf_queue_next(buf);
- cache_buf = qdf_mem_malloc(sizeof(*cache_buf));
- if (!cache_buf) {
- ol_txrx_err(
- "Failed to allocate buf to cache the rx frames");
- qdf_nbuf_free(buf);
- } else {
- /* Add NULL terminator */
- qdf_nbuf_set_next(buf, NULL);
- cache_buf->buf = buf;
- qdf_spin_lock_bh(&peer->bufq_lock);
- list_add_tail(&cache_buf->list,
- &peer->cached_bufq);
- qdf_spin_unlock_bh(&peer->bufq_lock);
- }
- buf = next_buf;
- }
+ if (ol_txrx_enqueue_rx_frames(&peer->bufq_info, rx_buf_list)
+ != QDF_STATUS_SUCCESS)
+ ol_txrx_err("failed to enqueue rx frm to cached_bufq");
} else {
#ifdef QCA_CONFIG_SMP
/*
@@ -4710,16 +4835,8 @@ void ol_rx_data_process(struct ol_txrx_peer_t *peer,
return;
drop_rx_buf:
- ol_txrx_info("Dropping rx packets");
- buf = rx_buf_list;
- while (buf) {
- next_buf = qdf_nbuf_queue_next(buf);
- if (pdev)
- TXRX_STATS_MSDU_INCR(pdev,
- rx.dropped_peer_invalid, buf);
- qdf_nbuf_free(buf);
- buf = next_buf;
- }
+ drop_count = ol_txrx_drop_nbuf_list(rx_buf_list);
+ ol_txrx_info("Dropped rx packets %u", drop_count);
}
/**
diff --git a/core/dp/txrx/ol_txrx_types.h b/core/dp/txrx/ol_txrx_types.h
index db7fd194b8f5..2668d898c4fa 100644
--- a/core/dp/txrx/ol_txrx_types.h
+++ b/core/dp/txrx/ol_txrx_types.h
@@ -1134,6 +1134,26 @@ typedef A_STATUS (*ol_tx_filter_func)(struct ol_txrx_msdu_info_t *
/* Allow 6000 ms to receive peer unmap events after peer is deleted */
#define OL_TXRX_PEER_UNMAP_TIMEOUT (6000)
+struct ol_txrx_cached_bufq_t {
+ /* cached_bufq is used to enqueue the pending RX frames from a peer
+ * before the peer is registered for data service. The list will be
+ * flushed to HDD once that station is registered.
+ */
+ struct list_head cached_bufq;
+ /* mutual exclusion lock to access the cached_bufq queue */
+ qdf_spinlock_t bufq_lock;
+ /* # entries in queue after which subsequent adds will be dropped */
+ uint32_t thresh;
+ /* # entries in present in cached_bufq */
+ uint32_t curr;
+ /* # max num of entries in the queue if bufq thresh was not in place */
+ uint32_t high_water_mark;
+ /* # max num of entries in the queue if we did not drop packets */
+ uint32_t qdepth_no_thresh;
+ /* # of packes (beyond threshold) dropped from cached_bufq */
+ uint32_t dropped;
+};
+
struct ol_txrx_peer_t {
struct ol_txrx_vdev_t *vdev;
@@ -1152,8 +1172,9 @@ struct ol_txrx_peer_t {
*/
enum ol_txrx_peer_state state;
qdf_spinlock_t peer_info_lock;
- qdf_spinlock_t bufq_lock;
- struct list_head cached_bufq;
+
+ /* Wrapper around the cached_bufq list */
+ struct ol_txrx_cached_bufq_t bufq_info;
ol_tx_filter_func tx_filter;