diff --git a/dp/inc/cdp_txrx_mob_def.h b/dp/inc/cdp_txrx_mob_def.h index 7af60f8a01..2107b77fa3 100644 --- a/dp/inc/cdp_txrx_mob_def.h +++ b/dp/inc/cdp_txrx_mob_def.h @@ -97,6 +97,11 @@ enum netif_action_type { WLAN_NETIF_CARRIER_OFF, WLAN_NETIF_PRIORITY_QUEUE_ON, WLAN_NETIF_PRIORITY_QUEUE_OFF, + WLAN_NETIF_VO_QUEUE_ON, + WLAN_NETIF_VO_QUEUE_OFF, + WLAN_NETIF_VI_QUEUE_ON, + WLAN_NETIF_VI_QUEUE_OFF, + WLAN_NETIF_BE_BK_QUEUE_OFF, WLAN_WAKE_NON_PRIORITY_QUEUE, WLAN_STOP_NON_PRIORITY_QUEUE, WLAN_NETIF_ACTION_TYPE_MAX, diff --git a/dp/wifi3.0/dp_tx_desc.h b/dp/wifi3.0/dp_tx_desc.h index 48ca397b19..cf15d83924 100644 --- a/dp/wifi3.0/dp_tx_desc.h +++ b/dp/wifi3.0/dp_tx_desc.h @@ -58,6 +58,11 @@ #define TX_DESC_LOCK_UNLOCK(lock) #define IS_TX_DESC_POOL_STATUS_INACTIVE(pool) \ ((pool)->status == FLOW_POOL_INACTIVE) +#ifdef QCA_AC_BASED_FLOW_CONTROL +#define TX_DESC_POOL_MEMBER_CLEAN(_tx_desc_pool) \ + dp_tx_flow_pool_member_clean(_tx_desc_pool) + +#else /* !QCA_AC_BASED_FLOW_CONTROL */ #define TX_DESC_POOL_MEMBER_CLEAN(_tx_desc_pool) \ do { \ (_tx_desc_pool)->elem_size = 0; \ @@ -68,6 +73,7 @@ do { \ (_tx_desc_pool)->stop_th = 0; \ (_tx_desc_pool)->status = FLOW_POOL_INACTIVE; \ } while (0) +#endif /* QCA_AC_BASED_FLOW_CONTROL */ #else /* !QCA_LL_TX_FLOW_CONTROL_V2 */ #define TX_DESC_LOCK_CREATE(lock) qdf_spinlock_create(lock) #define TX_DESC_LOCK_DESTROY(lock) qdf_spinlock_destroy(lock) @@ -153,7 +159,224 @@ void dp_tx_put_desc_flow_pool(struct dp_tx_desc_pool_s *pool, pool->avail_desc++; } +#ifdef QCA_AC_BASED_FLOW_CONTROL +/** + * dp_tx_flow_pool_member_clean() - Clean the members of TX flow pool + * + * @pool: flow pool + * + * Return: None + */ +static inline void +dp_tx_flow_pool_member_clean(struct dp_tx_desc_pool_s *pool) +{ + pool->elem_size = 0; + pool->freelist = NULL; + pool->pool_size = 0; + pool->avail_desc = 0; + qdf_mem_zero(pool->start_th, FL_TH_MAX); + qdf_mem_zero(pool->stop_th, FL_TH_MAX); + pool->status = FLOW_POOL_INACTIVE; +} + +/** + * dp_tx_is_threshold_reached() - Check if current avail desc meet threshold + * + * @pool: flow pool + * @avail_desc: available descriptor number + * + * Return: true if threshold is met, false if not + */ +static inline bool +dp_tx_is_threshold_reached(struct dp_tx_desc_pool_s *pool, uint16_t avail_desc) +{ + if (qdf_unlikely(avail_desc == pool->stop_th[DP_TH_BE_BK])) + return true; + else if (qdf_unlikely(avail_desc == pool->stop_th[DP_TH_VI])) + return true; + else if (qdf_unlikely(avail_desc == pool->stop_th[DP_TH_VO])) + return true; + else if (qdf_unlikely(avail_desc == pool->stop_th[DP_TH_HI])) + return true; + else + return false; +} + +/** + * dp_tx_desc_alloc() - Allocate a Software Tx descriptor from given pool + * + * @soc: Handle to DP SoC structure + * @desc_pool_id: ID of the flow control fool + * + * Return: TX descriptor allocated or NULL + */ +static inline struct dp_tx_desc_s * +dp_tx_desc_alloc(struct dp_soc *soc, uint8_t desc_pool_id) +{ + struct dp_tx_desc_s *tx_desc = NULL; + struct dp_tx_desc_pool_s *pool = &soc->tx_desc[desc_pool_id]; + bool is_pause = false; + enum netif_action_type act = WLAN_WAKE_NON_PRIORITY_QUEUE; + enum dp_fl_ctrl_threshold level = DP_TH_BE_BK; + + if (qdf_likely(pool)) { + qdf_spin_lock_bh(&pool->flow_pool_lock); + if (qdf_likely(pool->avail_desc)) { + tx_desc = dp_tx_get_desc_flow_pool(pool); + tx_desc->pool_id = desc_pool_id; + tx_desc->flags = DP_TX_DESC_FLAG_ALLOCATED; + is_pause = dp_tx_is_threshold_reached(pool, + pool->avail_desc); + + if (qdf_unlikely(is_pause)) { + switch (pool->status) { + case FLOW_POOL_ACTIVE_UNPAUSED: + /* pause network BE\BK queue */ + act = WLAN_NETIF_BE_BK_QUEUE_OFF; + level = DP_TH_BE_BK; + pool->status = FLOW_POOL_BE_BK_PAUSED; + break; + case FLOW_POOL_BE_BK_PAUSED: + /* pause network VI queue */ + act = WLAN_NETIF_VI_QUEUE_OFF; + level = DP_TH_VI; + pool->status = FLOW_POOL_VI_PAUSED; + break; + case FLOW_POOL_VI_PAUSED: + /* pause network VO queue */ + act = WLAN_NETIF_VO_QUEUE_OFF; + level = DP_TH_VO; + pool->status = FLOW_POOL_VO_PAUSED; + break; + case FLOW_POOL_VO_PAUSED: + /* pause network HI PRI queue */ + act = WLAN_NETIF_PRIORITY_QUEUE_OFF; + level = DP_TH_HI; + pool->status = FLOW_POOL_ACTIVE_PAUSED; + break; + default: + QDF_TRACE(QDF_MODULE_ID_DP, + QDF_TRACE_LEVEL_ERROR, + "%s %d pool is %d status!", + __func__, __LINE__, + pool->status); + break; + } + pool->latest_pause_time[level] = + qdf_get_system_timestamp(); + soc->pause_cb(desc_pool_id, + act, WLAN_DATA_FLOW_CONTROL); + qdf_spin_unlock_bh(&pool->flow_pool_lock); + } else { + qdf_spin_unlock_bh(&pool->flow_pool_lock); + } + } else { + pool->pkt_drop_no_desc++; + qdf_spin_unlock_bh(&pool->flow_pool_lock); + } + } else { + soc->pool_stats.pkt_drop_no_pool++; + } + + return tx_desc; +} + +/** + * dp_tx_desc_free() - Fee a tx descriptor and attach it to free list + * + * @soc: Handle to DP SoC structure + * @tx_desc: the tx descriptor to be freed + * @desc_pool_id: ID of the flow control fool + * + * Return: None + */ +static inline void +dp_tx_desc_free(struct dp_soc *soc, struct dp_tx_desc_s *tx_desc, + uint8_t desc_pool_id) +{ + struct dp_tx_desc_pool_s *pool = &soc->tx_desc[desc_pool_id]; + qdf_time_t unpause_time = qdf_get_system_timestamp(), pause_dur; + enum netif_action_type act = WLAN_WAKE_ALL_NETIF_QUEUE; + + qdf_spin_lock_bh(&pool->flow_pool_lock); + tx_desc->flags = 0; + dp_tx_put_desc_flow_pool(pool, tx_desc); + switch (pool->status) { + case FLOW_POOL_ACTIVE_PAUSED: + if (pool->avail_desc > pool->start_th[DP_TH_HI]) { + act = WLAN_NETIF_PRIORITY_QUEUE_ON; + pool->status = FLOW_POOL_VO_PAUSED; + + /* Update maxinum pause duration for HI queue */ + pause_dur = unpause_time - + pool->latest_pause_time[DP_TH_HI]; + if (pool->max_pause_time[DP_TH_HI] < pause_dur) + pool->max_pause_time[DP_TH_HI] = pause_dur; + } + break; + case FLOW_POOL_VO_PAUSED: + if (pool->avail_desc > pool->start_th[DP_TH_VO]) { + act = WLAN_NETIF_VO_QUEUE_ON; + pool->status = FLOW_POOL_VI_PAUSED; + + /* Update maxinum pause duration for VO queue */ + pause_dur = unpause_time - + pool->latest_pause_time[DP_TH_VO]; + if (pool->max_pause_time[DP_TH_VO] < pause_dur) + pool->max_pause_time[DP_TH_VO] = pause_dur; + } + break; + case FLOW_POOL_VI_PAUSED: + if (pool->avail_desc > pool->start_th[DP_TH_VI]) { + act = WLAN_NETIF_VI_QUEUE_ON; + pool->status = FLOW_POOL_BE_BK_PAUSED; + + /* Update maxinum pause duration for VI queue */ + pause_dur = unpause_time - + pool->latest_pause_time[DP_TH_VI]; + if (pool->max_pause_time[DP_TH_VI] < pause_dur) + pool->max_pause_time[DP_TH_VI] = pause_dur; + } + break; + case FLOW_POOL_BE_BK_PAUSED: + if (pool->avail_desc > pool->start_th[DP_TH_BE_BK]) { + act = WLAN_WAKE_NON_PRIORITY_QUEUE; + pool->status = FLOW_POOL_ACTIVE_UNPAUSED; + + /* Update maxinum pause duration for BE_BK queue */ + pause_dur = unpause_time - + pool->latest_pause_time[DP_TH_BE_BK]; + if (pool->max_pause_time[DP_TH_BE_BK] < pause_dur) + pool->max_pause_time[DP_TH_BE_BK] = pause_dur; + } + break; + case FLOW_POOL_INVALID: + if (pool->avail_desc == pool->pool_size) { + dp_tx_desc_pool_free(soc, desc_pool_id); + qdf_spin_unlock_bh(&pool->flow_pool_lock); + QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, + "%s %d pool is freed!!", + __func__, __LINE__); + return; + } + break; + + case FLOW_POOL_ACTIVE_UNPAUSED: + break; + default: + QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, + "%s %d pool is INACTIVE State!!", + __func__, __LINE__); + break; + }; + + if (act != WLAN_WAKE_ALL_NETIF_QUEUE) + soc->pause_cb(pool->flow_pool_id, + act, WLAN_DATA_FLOW_CONTROL); + qdf_spin_unlock_bh(&pool->flow_pool_lock); +} +#else /* QCA_AC_BASED_FLOW_CONTROL */ /** * dp_tx_desc_alloc() - Allocate a Software Tx Descriptor from given pool * @@ -260,6 +483,8 @@ out: */ hif_pm_runtime_put(soc->hif_handle); } + +#endif /* QCA_AC_BASED_FLOW_CONTROL */ #else /* QCA_LL_TX_FLOW_CONTROL_V2 */ static inline void dp_tx_flow_control_init(struct dp_soc *handle) diff --git a/dp/wifi3.0/dp_tx_flow_control.c b/dp/wifi3.0/dp_tx_flow_control.c index fa32449caf..eed23c6a0d 100644 --- a/dp/wifi3.0/dp_tx_flow_control.c +++ b/dp/wifi3.0/dp_tx_flow_control.c @@ -30,6 +30,142 @@ #define INVALID_FLOW_ID 0xFF #define MAX_INVALID_BIN 3 +#ifdef QCA_AC_BASED_FLOW_CONTROL +/** + * dp_tx_initialize_threshold() - Threshold of flow Pool initialization + * @pool: flow_pool + * @stop_threshold: stop threshold of certian AC + * @start_threshold: start threshold of certian AC + * @flow_pool_size: flow pool size + * + * Return: none + */ +static inline void +dp_tx_initialize_threshold(struct dp_tx_desc_pool_s *pool, + uint32_t start_threshold, + uint32_t stop_threshold, + uint16_t flow_pool_size) +{ + /* BE_BK threshold is same as previous threahold */ + pool->start_th[DP_TH_BE_BK] = (start_threshold + * flow_pool_size) / 100; + pool->stop_th[DP_TH_BE_BK] = (stop_threshold + * flow_pool_size) / 100; + + /* Update VI threshold based on BE_BK threashold */ + pool->start_th[DP_TH_VI] = (pool->start_th[DP_TH_BE_BK] + * FL_TH_VI_PERCENTAGE) / 100; + pool->stop_th[DP_TH_VI] = (pool->stop_th[DP_TH_BE_BK] + * FL_TH_VI_PERCENTAGE) / 100; + + /* Update VO threshold based on BE_BK threashold */ + pool->start_th[DP_TH_VO] = (pool->start_th[DP_TH_BE_BK] + * FL_TH_VO_PERCENTAGE) / 100; + pool->stop_th[DP_TH_VO] = (pool->stop_th[DP_TH_BE_BK] + * FL_TH_VO_PERCENTAGE) / 100; + + /* Update High Priority threshold based on BE_BK threashold */ + pool->start_th[DP_TH_HI] = (pool->start_th[DP_TH_BE_BK] + * FL_TH_HI_PERCENTAGE) / 100; + pool->stop_th[DP_TH_HI] = (pool->stop_th[DP_TH_BE_BK] + * FL_TH_HI_PERCENTAGE) / 100; + + QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, + "%s: tx flow control threshold is set, pool size is %d", + __func__, flow_pool_size); +} + +/** + * dp_tx_flow_pool_reattach() - Reattach flow_pool + * @pool: flow_pool + * + * Return: none + */ +static inline void +dp_tx_flow_pool_reattach(struct dp_tx_desc_pool_s *pool) +{ + QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, + "%s: flow pool already allocated, attached %d times", + __func__, pool->pool_create_cnt); + + if (pool->avail_desc > pool->start_th[DP_TH_BE_BK]) + pool->status = FLOW_POOL_ACTIVE_UNPAUSED; + if (pool->avail_desc <= pool->start_th[DP_TH_BE_BK] && + pool->avail_desc > pool->start_th[DP_TH_VI]) + pool->status = FLOW_POOL_BE_BK_PAUSED; + else if (pool->avail_desc <= pool->start_th[DP_TH_VI] && + pool->avail_desc > pool->start_th[DP_TH_VO]) + pool->status = FLOW_POOL_VI_PAUSED; + else if (pool->avail_desc <= pool->start_th[DP_TH_VO] && + pool->avail_desc > pool->start_th[DP_TH_HI]) + pool->status = FLOW_POOL_VO_PAUSED; + else + pool->status = FLOW_POOL_ACTIVE_PAUSED; + + pool->pool_create_cnt++; +} + +/** + * dp_tx_flow_pool_dump_threshold() - Dump threshold of the flow_pool + * @pool: flow_pool + * + * Return: none + */ +static inline void +dp_tx_flow_pool_dump_threshold(struct dp_tx_desc_pool_s *pool) +{ + int i; + + for (i = 0; i < FL_TH_MAX; i++) { + QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, + "Level %d :: Start threshold %d :: Stop threshold %d", + i, pool->start_th[i], pool->stop_th[i]); + QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, + "Level %d :: Maximun pause time %lu ms", + i, pool->max_pause_time[i]); + QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, + "Level %d :: Latest pause timestamp %lu", + i, pool->latest_pause_time[i]); + } +} + +#else +static inline void +dp_tx_initialize_threshold(struct dp_tx_desc_pool_s *pool, + uint32_t start_threshold, + uint32_t stop_threshold, + uint16_t flow_pool_size) + +{ + /* INI is in percentage so divide by 100 */ + pool->start_th = (start_threshold * flow_pool_size) / 100; + pool->stop_th = (stop_threshold * flow_pool_size) / 100; +} + +static inline void +dp_tx_flow_pool_reattach(struct dp_tx_desc_pool_s *pool) +{ + QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, + "%s: flow pool already allocated, attached %d times", + __func__, pool->pool_create_cnt); + if (pool->avail_desc > pool->start_th) + pool->status = FLOW_POOL_ACTIVE_UNPAUSED; + else + pool->status = FLOW_POOL_ACTIVE_PAUSED; + + pool->pool_create_cnt++; +} + +static inline void +dp_tx_flow_pool_dump_threshold(struct dp_tx_desc_pool_s *pool) +{ + QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, + "Start threshold %d :: Stop threshold %d", + pool->start_th, pool->stop_th); +} + +#endif + /** * dp_tx_dump_flow_pool_info() - dump global_pool and flow_pool info * @@ -74,9 +210,7 @@ void dp_tx_dump_flow_pool_info(void *ctx) QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, "Total %d :: Available %d", tmp_pool.pool_size, tmp_pool.avail_desc); - QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, - "Start threshold %d :: Stop threshold %d", - tmp_pool.start_th, tmp_pool.stop_th); + dp_tx_flow_pool_dump_threshold(&tmp_pool); QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, "Member flow_id %d :: flow_type %d", tmp_pool.flow_pool_id, tmp_pool.flow_type); @@ -129,16 +263,7 @@ struct dp_tx_desc_pool_s *dp_tx_create_flow_pool(struct dp_soc *soc, pool = &soc->tx_desc[flow_pool_id]; qdf_spin_lock_bh(&pool->flow_pool_lock); if ((pool->status != FLOW_POOL_INACTIVE) || pool->pool_create_cnt) { - QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, - "%s: flow pool already allocated, attached %d times", - __func__, pool->pool_create_cnt); - if (pool->avail_desc > pool->start_th) - pool->status = FLOW_POOL_ACTIVE_UNPAUSED; - else - pool->status = FLOW_POOL_ACTIVE_PAUSED; - - pool->pool_create_cnt++; - + dp_tx_flow_pool_reattach(pool); qdf_spin_unlock_bh(&pool->flow_pool_lock); return pool; } @@ -156,9 +281,8 @@ struct dp_tx_desc_pool_s *dp_tx_create_flow_pool(struct dp_soc *soc, pool->pool_size = flow_pool_size; pool->avail_desc = flow_pool_size; pool->status = FLOW_POOL_ACTIVE_UNPAUSED; - /* INI is in percentage so divide by 100 */ - pool->start_th = (start_threshold * flow_pool_size)/100; - pool->stop_th = (stop_threshold * flow_pool_size)/100; + dp_tx_initialize_threshold(pool, start_threshold, stop_threshold, + flow_pool_size); pool->pool_create_cnt++; qdf_spin_unlock_bh(&pool->flow_pool_lock); diff --git a/dp/wifi3.0/dp_types.h b/dp/wifi3.0/dp_types.h index b5ee817c7e..64450d4604 100644 --- a/dp/wifi3.0/dp_types.h +++ b/dp/wifi3.0/dp_types.h @@ -155,6 +155,25 @@ union dp_rx_desc_list_elem_t; #define DP_HW2SW_MACID(id) ((id) > 0 ? ((id) - 1) : 0) #define DP_MAC_ADDR_LEN 6 +/** + * Number of Tx Queues + * enum and macro to define how many threshold levels is used + * for the AC based flow control + */ +#ifdef QCA_AC_BASED_FLOW_CONTROL +enum dp_fl_ctrl_threshold { + DP_TH_BE_BK = 0, + DP_TH_VI, + DP_TH_VO, + DP_TH_HI, +}; + +#define FL_TH_MAX (4) +#define FL_TH_VI_PERCENTAGE (80) +#define FL_TH_VO_PERCENTAGE (60) +#define FL_TH_HI_PERCENTAGE (40) +#endif + /** * enum dp_intr_mode * @DP_INTR_LEGACY: Legacy/Line interrupts, for WIN @@ -314,8 +333,11 @@ struct dp_tx_desc_s { enum flow_pool_status { FLOW_POOL_ACTIVE_UNPAUSED = 0, FLOW_POOL_ACTIVE_PAUSED = 1, - FLOW_POOL_INVALID = 2, - FLOW_POOL_INACTIVE = 3, + FLOW_POOL_BE_BK_PAUSED = 2, + FLOW_POOL_VI_PAUSED = 3, + FLOW_POOL_VO_PAUSED = 4, + FLOW_POOL_INVALID = 5, + FLOW_POOL_INACTIVE = 6, }; /** @@ -377,8 +399,15 @@ struct dp_tx_desc_pool_s { uint16_t avail_desc; enum flow_pool_status status; enum htt_flow_type flow_type; +#ifdef QCA_AC_BASED_FLOW_CONTROL + uint16_t stop_th[FL_TH_MAX]; + uint16_t start_th[FL_TH_MAX]; + qdf_time_t max_pause_time[FL_TH_MAX]; + qdf_time_t latest_pause_time[FL_TH_MAX]; +#else uint16_t stop_th; uint16_t start_th; +#endif uint16_t pkt_drop_no_desc; qdf_spinlock_t flow_pool_lock; uint8_t pool_create_cnt;