diff --git a/core/dp/ol/inc/ol_txrx_ctrl_api.h b/core/dp/ol/inc/ol_txrx_ctrl_api.h index 416d31745510..ab7519f17a4f 100644 --- a/core/dp/ol/inc/ol_txrx_ctrl_api.h +++ b/core/dp/ol/inc/ol_txrx_ctrl_api.h @@ -487,6 +487,16 @@ bool ol_txrx_get_ocb_peer(struct ol_txrx_pdev_t *pdev, void ol_tx_set_is_mgmt_over_wmi_enabled(uint8_t value); uint8_t ol_tx_get_is_mgmt_over_wmi_enabled(void); +#ifdef QCA_LL_TX_FLOW_CONTROL_RESIZE +void ol_tx_flow_pool_resize_handler(uint8_t flow_pool_id, + uint16_t flow_pool_size); +#else +static inline void ol_tx_flow_pool_resize_handler(uint8_t flow_pool_id, + uint16_t flow_pool_size) +{ +} +#endif + /* TX FLOW Control related functions */ #ifdef QCA_LL_TX_FLOW_CONTROL_V2 #define TX_FLOW_MGMT_POOL_ID 0xEF diff --git a/core/dp/txrx/ol_tx_desc.c b/core/dp/txrx/ol_tx_desc.c index 2d857c7da69d..6785a39affce 100644 --- a/core/dp/txrx/ol_tx_desc.c +++ b/core/dp/txrx/ol_tx_desc.c @@ -439,6 +439,40 @@ void ol_tx_desc_free(struct ol_txrx_pdev_t *pdev, struct ol_tx_desc_t *tx_desc) } #else + +/** + * ol_tx_update_free_desc_to_pool() - update free desc to pool + * @pdev: pdev handle + * @tx_desc: descriptor + * + * Return : 1 desc distribution required / 0 don't need distribution + */ +#ifdef QCA_LL_TX_FLOW_CONTROL_RESIZE +static inline bool ol_tx_update_free_desc_to_pool(struct ol_txrx_pdev_t *pdev, + struct ol_tx_desc_t *tx_desc) +{ + struct ol_tx_flow_pool_t *pool = tx_desc->pool; + bool distribute_desc = false; + + if (unlikely(pool->overflow_desc)) { + ol_tx_put_desc_global_pool(pdev, tx_desc); + --pool->overflow_desc; + distribute_desc = true; + } else { + ol_tx_put_desc_flow_pool(pool, tx_desc); + } + + return distribute_desc; +} +#else +static inline bool ol_tx_update_free_desc_to_pool(struct ol_txrx_pdev_t *pdev, + struct ol_tx_desc_t *tx_desc) +{ + ol_tx_put_desc_flow_pool(tx_desc->pool, tx_desc); + return false; +} +#endif + /** * ol_tx_desc_free() - put descriptor to pool freelist * @pdev: pdev handle @@ -448,12 +482,14 @@ void ol_tx_desc_free(struct ol_txrx_pdev_t *pdev, struct ol_tx_desc_t *tx_desc) */ void ol_tx_desc_free(struct ol_txrx_pdev_t *pdev, struct ol_tx_desc_t *tx_desc) { + bool distribute_desc = false; struct ol_tx_flow_pool_t *pool = tx_desc->pool; qdf_spin_lock_bh(&pool->flow_pool_lock); ol_tx_desc_free_common(pdev, tx_desc); - ol_tx_put_desc_flow_pool(pool, tx_desc); + distribute_desc = ol_tx_update_free_desc_to_pool(pdev, tx_desc); + switch (pool->status) { case FLOW_POOL_ACTIVE_PAUSED: if (pool->avail_desc > pool->start_priority_th) { @@ -491,6 +527,9 @@ void ol_tx_desc_free(struct ol_txrx_pdev_t *pdev, struct ol_tx_desc_t *tx_desc) qdf_spin_unlock_bh(&pool->flow_pool_lock); + if (unlikely(distribute_desc)) + ol_tx_distribute_descs_to_deficient_pools_from_global_pool(); + } #endif diff --git a/core/dp/txrx/ol_tx_desc.h b/core/dp/txrx/ol_tx_desc.h index a1f670765b12..dc1bebfab889 100644 --- a/core/dp/txrx/ol_tx_desc.h +++ b/core/dp/txrx/ol_tx_desc.h @@ -289,6 +289,17 @@ void ol_tx_put_desc_global_pool(struct ol_txrx_pdev_t *pdev, #ifdef QCA_LL_TX_FLOW_CONTROL_V2 + +#ifdef QCA_LL_TX_FLOW_CONTROL_RESIZE +int ol_tx_distribute_descs_to_deficient_pools_from_global_pool(void); +#else +static inline +int ol_tx_distribute_descs_to_deficient_pools_from_global_pool(void) +{ + return 0; +} +#endif + int ol_tx_free_invalid_flow_pool(struct ol_tx_flow_pool_t *pool); /** * ol_tx_get_desc_flow_pool() - get descriptor from flow pool diff --git a/core/dp/txrx/ol_txrx_flow_control.c b/core/dp/txrx/ol_txrx_flow_control.c index fc1834c1f678..307cc2959702 100644 --- a/core/dp/txrx/ol_txrx_flow_control.c +++ b/core/dp/txrx/ol_txrx_flow_control.c @@ -208,6 +208,8 @@ static int ol_tx_delete_flow_pool(struct ol_tx_flow_pool_t *pool, bool force) } qdf_spin_unlock_bh(&pdev->tx_mutex); + ol_tx_distribute_descs_to_deficient_pools_from_global_pool(); + return 0; } @@ -301,14 +303,18 @@ void ol_tx_dump_flow_pool_info(void *ctx) QDF_ASSERT(0); /* traceback */ return; } + ol_txrx_log(QDF_TRACE_LEVEL_INFO_LOW, - "Global total %d :: avail %d invalid flow_pool %d " - "maps %d pool unmaps %d pkt drops %d", + "Global total %d :: avail %d invalid flow_pool %d ", pdev->tx_desc.pool_size, pdev->tx_desc.num_free, - pdev->tx_desc.num_invalid_bin, + pdev->tx_desc.num_invalid_bin); + + ol_txrx_log(QDF_TRACE_LEVEL_INFO_LOW, + "maps %d pool unmaps %d pool resize %d pkt drops %d", pdev->pool_stats.pool_map_count, pdev->pool_stats.pool_unmap_count, + pdev->pool_stats.pool_resize_count, pdev->pool_stats.pkt_drop_no_pool); /* * Nested spin lock. @@ -334,10 +340,10 @@ void ol_tx_dump_flow_pool_info(void *ctx) ol_tx_flow_pool_status_to_str(tmp_pool.status), tmp_pool.member_flow_id, tmp_pool.flow_type); ol_txrx_log(QDF_TRACE_LEVEL_INFO_LOW, - "total %d :: available %d :: deficient %d :: " - "pkt dropped (no desc) %d", + "total %d :: available %d :: deficient %d :: overflow %d :: pkt dropped (no desc) %d", tmp_pool.flow_pool_size, tmp_pool.avail_desc, tmp_pool.deficient_desc, + tmp_pool.overflow_desc, tmp_pool.pkt_drop_no_desc); ol_txrx_log(QDF_TRACE_LEVEL_INFO_LOW, "thresh: start %d stop %d prio start %d prio stop %d", @@ -479,7 +485,6 @@ ol_tx_distribute_descs_to_deficient_pools(struct ol_tx_flow_pool_t *src_pool) return 0; } - /** * ol_tx_create_flow_pool() - create flow pool * @flow_pool_id: flow pool id @@ -549,6 +554,8 @@ struct ol_tx_flow_pool_t *ol_tx_create_flow_pool(uint8_t flow_pool_id, pool->freelist = temp_list; pool->avail_desc = size; pool->deficient_desc = pool->flow_pool_size - pool->avail_desc; + /* used for resize pool*/ + pool->overflow_desc = 0; /* Add flow_pool to flow_pool_list */ qdf_spin_lock_bh(&pdev->tx_desc.flow_pool_list_lock); @@ -627,7 +634,6 @@ static struct ol_tx_flow_pool_t *ol_tx_get_flow_pool(uint8_t flow_pool_id) return pool; } - /** * ol_tx_flow_pool_vdev_map() - Map flow_pool with vdev * @pool: flow_pool @@ -797,4 +803,342 @@ void ol_tx_flow_pool_unmap_handler(uint8_t flow_id, uint8_t flow_type, ol_tx_dec_pool_ref(pool, false); } +#ifdef QCA_LL_TX_FLOW_CONTROL_RESIZE +/** + * ol_tx_distribute_descs_to_deficient_pools_from_global_pool() + * + * Distribute descriptors of global pool to all + * deficient pools as per need. + * + * Return: 0 for success + */ +int ol_tx_distribute_descs_to_deficient_pools_from_global_pool(void) +{ + struct ol_txrx_pdev_t *pdev = cds_get_context(QDF_MODULE_ID_TXRX); + struct ol_tx_flow_pool_t *dst_pool = NULL; + struct ol_tx_flow_pool_t *tmp_pool = NULL; + uint16_t total_desc_req = 0; + uint16_t desc_move_count = 0; + uint16_t temp_count = 0, i; + union ol_tx_desc_list_elem_t *temp_list = NULL; + struct ol_tx_desc_t *tx_desc; + uint8_t free_invalid_pool = 0; + if (!pdev) { + ol_txrx_err( + "%s: pdev is NULL\n", __func__); + return -EINVAL; + } + + /* Nested locks: maintain flow_pool_list_lock->flow_pool_lock */ + /* find out total deficient desc required */ + qdf_spin_lock_bh(&pdev->tx_desc.flow_pool_list_lock); + TAILQ_FOREACH(dst_pool, &pdev->tx_desc.flow_pool_list, + flow_pool_list_elem) { + qdf_spin_lock_bh(&dst_pool->flow_pool_lock); + total_desc_req += dst_pool->deficient_desc; + qdf_spin_unlock_bh(&dst_pool->flow_pool_lock); + } + qdf_spin_unlock_bh(&pdev->tx_desc.flow_pool_list_lock); + + qdf_spin_lock_bh(&pdev->tx_mutex); + desc_move_count = (pdev->tx_desc.num_free >= total_desc_req) ? + total_desc_req : pdev->tx_desc.num_free; + + for (i = 0; i < desc_move_count; i++) { + tx_desc = ol_tx_get_desc_global_pool(pdev); + ((union ol_tx_desc_list_elem_t *)tx_desc)->next = temp_list; + temp_list = (union ol_tx_desc_list_elem_t *)tx_desc; + } + qdf_spin_unlock_bh(&pdev->tx_mutex); + + if (!desc_move_count) + return 0; + + /* destribute desc to deficient pool */ + qdf_spin_lock_bh(&pdev->tx_desc.flow_pool_list_lock); + TAILQ_FOREACH(dst_pool, &pdev->tx_desc.flow_pool_list, + flow_pool_list_elem) { + qdf_spin_lock_bh(&dst_pool->flow_pool_lock); + if (dst_pool->deficient_desc) { + temp_count = + (dst_pool->deficient_desc > desc_move_count) ? + desc_move_count : dst_pool->deficient_desc; + + desc_move_count -= temp_count; + for (i = 0; i < temp_count; i++) { + tx_desc = &temp_list->tx_desc; + temp_list = temp_list->next; + ol_tx_put_desc_flow_pool(dst_pool, tx_desc); + } + + if (dst_pool->status == FLOW_POOL_ACTIVE_PAUSED) { + if (dst_pool->avail_desc > dst_pool->start_th) { + pdev->pause_cb(dst_pool->member_flow_id, + WLAN_WAKE_ALL_NETIF_QUEUE, + WLAN_DATA_FLOW_CONTROL); + dst_pool->status = + FLOW_POOL_ACTIVE_UNPAUSED; + } + } else if ((dst_pool->status == FLOW_POOL_INVALID) && + (dst_pool->avail_desc == + dst_pool->flow_pool_size)) { + free_invalid_pool = 1; + tmp_pool = dst_pool; + } + } + qdf_spin_unlock_bh(&dst_pool->flow_pool_lock); + if (desc_move_count == 0) + break; + } + qdf_spin_unlock_bh(&pdev->tx_desc.flow_pool_list_lock); + + if (free_invalid_pool && tmp_pool) + ol_tx_free_invalid_flow_pool(tmp_pool); + + return 0; +} + +/** + * ol_tx_flow_pool_update_queue_state() - update network queue for pool based on + * new available count. + * @pool : pool handle + * + * Return : none + */ +static void ol_tx_flow_pool_update_queue_state(struct ol_txrx_pdev_t *pdev, + struct ol_tx_flow_pool_t *pool) +{ + qdf_spin_lock_bh(&pool->flow_pool_lock); + if (pool->avail_desc > pool->start_th) { + pool->status = FLOW_POOL_ACTIVE_UNPAUSED; + qdf_spin_unlock_bh(&pool->flow_pool_lock); + pdev->pause_cb(pool->member_flow_id, + WLAN_WAKE_ALL_NETIF_QUEUE, + WLAN_DATA_FLOW_CONTROL); + } else if (pool->avail_desc < pool->stop_th && + pool->avail_desc >= pool->stop_priority_th) { + pool->status = FLOW_POOL_NON_PRIO_PAUSED; + qdf_spin_unlock_bh(&pool->flow_pool_lock); + pdev->pause_cb(pool->member_flow_id, + WLAN_STOP_NON_PRIORITY_QUEUE, + WLAN_DATA_FLOW_CONTROL); + pdev->pause_cb(pool->member_flow_id, + WLAN_NETIF_PRIORITY_QUEUE_ON, + WLAN_DATA_FLOW_CONTROL); + } else if (pool->avail_desc < pool->stop_priority_th) { + pool->status = FLOW_POOL_ACTIVE_PAUSED; + qdf_spin_unlock_bh(&pool->flow_pool_lock); + pdev->pause_cb(pool->member_flow_id, + WLAN_STOP_ALL_NETIF_QUEUE, + WLAN_DATA_FLOW_CONTROL); + } else { + qdf_spin_unlock_bh(&pool->flow_pool_lock); + } +} + +/** + * ol_tx_flow_pool_update() - update pool parameters with new size + * @pool : pool handle + * @new_pool_size : new pool size + * @deficient_count : deficient count + * @overflow_count : overflow count + * + * Return : none + */ +static void ol_tx_flow_pool_update(struct ol_tx_flow_pool_t *pool, + uint16_t new_pool_size, + uint16_t deficient_count, + uint16_t overflow_count) +{ + struct ol_txrx_pdev_t *pdev = cds_get_context(QDF_MODULE_ID_TXRX); + uint32_t stop_threshold = + ol_cfg_get_tx_flow_stop_queue_th(pdev->ctrl_pdev); + uint32_t start_threshold = stop_threshold + + ol_cfg_get_tx_flow_start_queue_offset(pdev->ctrl_pdev); + + pool->flow_pool_size = new_pool_size; + pool->start_th = (start_threshold * new_pool_size) / 100; + pool->stop_th = (stop_threshold * new_pool_size) / 100; + pool->stop_priority_th = (TX_PRIORITY_TH * pool->stop_th) / 100; + if (pool->stop_priority_th >= MAX_TSO_SEGMENT_DESC) + pool->stop_priority_th -= MAX_TSO_SEGMENT_DESC; + + pool->start_priority_th = (TX_PRIORITY_TH * pool->start_th) / 100; + if (pool->start_priority_th >= MAX_TSO_SEGMENT_DESC) + pool->start_priority_th -= MAX_TSO_SEGMENT_DESC; + + if (deficient_count) + pool->deficient_desc = deficient_count; + + if (overflow_count) + pool->overflow_desc = overflow_count; +} + +/** + * ol_tx_flow_pool_resize() - resize pool with new size + * @pool: pool pointer + * @new_pool_size: new pool size + * + * Return: none + */ +static void ol_tx_flow_pool_resize(struct ol_tx_flow_pool_t *pool, + uint16_t new_pool_size) +{ + struct ol_txrx_pdev_t *pdev = cds_get_context(QDF_MODULE_ID_TXRX); + uint16_t diff = 0, overflow_count = 0, deficient_count = 0; + uint16_t move_desc_to_global = 0, move_desc_from_global = 0; + union ol_tx_desc_list_elem_t *temp_list = NULL; + int i = 0, update_done = 0; + struct ol_tx_desc_t *tx_desc = NULL; + uint16_t temp = 0; + + qdf_spin_lock_bh(&pool->flow_pool_lock); + if (pool->flow_pool_size == new_pool_size) { + qdf_spin_unlock_bh(&pool->flow_pool_lock); + ol_txrx_info("pool resize received with same size"); + return; + } + qdf_spin_unlock_bh(&pool->flow_pool_lock); + + /* Reduce pool size */ + /* start_priority_th desc should available after reduction */ + qdf_spin_lock_bh(&pool->flow_pool_lock); + if (pool->flow_pool_size > new_pool_size) { + diff = pool->flow_pool_size - new_pool_size; + diff += pool->overflow_desc; + pool->overflow_desc = 0; + temp = QDF_MIN(pool->deficient_desc, diff); + pool->deficient_desc -= temp; + diff -= temp; + + if (diff) { + /* Have enough descriptors */ + if (pool->avail_desc >= + (diff + pool->start_priority_th)) { + move_desc_to_global = diff; + } + /* Do not have enough descriptors */ + else if (pool->avail_desc > pool->start_priority_th) { + move_desc_to_global = pool->avail_desc - + pool->start_priority_th; + overflow_count = diff - move_desc_to_global; + } + + /* Move desc to temp_list */ + for (i = 0; i < move_desc_to_global; i++) { + tx_desc = ol_tx_get_desc_flow_pool(pool); + ((union ol_tx_desc_list_elem_t *)tx_desc)->next + = temp_list; + temp_list = + (union ol_tx_desc_list_elem_t *)tx_desc; + } + } + + /* update pool size and threshold */ + ol_tx_flow_pool_update(pool, new_pool_size, 0, overflow_count); + update_done = 1; + } + qdf_spin_unlock_bh(&pool->flow_pool_lock); + + if (move_desc_to_global && temp_list) { + /* put free descriptors to global pool */ + qdf_spin_lock_bh(&pdev->tx_mutex); + for (i = 0; i < move_desc_to_global; i++) { + tx_desc = &temp_list->tx_desc; + temp_list = temp_list->next; + ol_tx_put_desc_global_pool(pdev, tx_desc); + } + qdf_spin_unlock_bh(&pdev->tx_mutex); + } + + if (update_done) + goto update_done; + + /* Increase pool size */ + qdf_spin_lock_bh(&pool->flow_pool_lock); + if (pool->flow_pool_size < new_pool_size) { + diff = new_pool_size - pool->flow_pool_size; + diff += pool->deficient_desc; + pool->deficient_desc = 0; + temp = QDF_MIN(pool->overflow_desc, diff); + pool->overflow_desc -= temp; + diff -= temp; + } + qdf_spin_unlock_bh(&pool->flow_pool_lock); + + if (diff) { + /* take descriptors from global pool */ + qdf_spin_lock_bh(&pdev->tx_mutex); + + if (pdev->tx_desc.num_free >= diff) { + move_desc_from_global = diff; + } else { + move_desc_from_global = pdev->tx_desc.num_free; + deficient_count = diff - move_desc_from_global; + } + + for (i = 0; i < move_desc_from_global; i++) { + tx_desc = ol_tx_get_desc_global_pool(pdev); + ((union ol_tx_desc_list_elem_t *)tx_desc)->next = + temp_list; + temp_list = (union ol_tx_desc_list_elem_t *)tx_desc; + } + qdf_spin_unlock_bh(&pdev->tx_mutex); + } + /* update desc to pool */ + qdf_spin_lock_bh(&pool->flow_pool_lock); + if (move_desc_from_global && temp_list) { + for (i = 0; i < move_desc_from_global; i++) { + tx_desc = &temp_list->tx_desc; + temp_list = temp_list->next; + ol_tx_put_desc_flow_pool(pool, tx_desc); + } + } + /* update pool size and threshold */ + ol_tx_flow_pool_update(pool, new_pool_size, deficient_count, 0); + qdf_spin_unlock_bh(&pool->flow_pool_lock); + +update_done: + + ol_tx_flow_pool_update_queue_state(pdev, pool); +} + +/** + * ol_tx_flow_pool_resize_handler() - Resize pool with new size + * @flow_pool_id: pool id + * @flow_pool_size: pool size + * + * Process below target to host message + * HTT_T2H_MSG_TYPE_FLOW_POOL_RESIZE + * + * Return: none + */ +void ol_tx_flow_pool_resize_handler(uint8_t flow_pool_id, + uint16_t flow_pool_size) +{ + struct ol_txrx_pdev_t *pdev = cds_get_context(QDF_MODULE_ID_TXRX); + struct ol_tx_flow_pool_t *pool; + + ol_txrx_dbg("%s: flow_pool_id %d flow_pool_size %d\n", + __func__, flow_pool_id, flow_pool_size); + + if (qdf_unlikely(!pdev)) { + ol_txrx_err( + "%s: pdev is NULL", __func__); + return; + } + pdev->pool_stats.pool_resize_count++; + + pool = ol_tx_get_flow_pool(flow_pool_id); + if (!pool) { + ol_txrx_err("%s: resize for flow_pool %d size %d failed\n", + __func__, flow_pool_id, flow_pool_size); + return; + } + + ol_tx_inc_pool_ref(pool); + ol_tx_flow_pool_resize(pool, flow_pool_size); + ol_tx_dec_pool_ref(pool, false); +} +#endif diff --git a/core/dp/txrx/ol_txrx_types.h b/core/dp/txrx/ol_txrx_types.h index fa906731f2bd..f3ec74c4cbc3 100644 --- a/core/dp/txrx/ol_txrx_types.h +++ b/core/dp/txrx/ol_txrx_types.h @@ -456,11 +456,13 @@ enum flow_pool_status { * struct ol_txrx_pool_stats - flow pool related statistics * @pool_map_count: flow pool map received * @pool_unmap_count: flow pool unmap received + * @pool_resize_count: flow pool resize command received * @pkt_drop_no_pool: packets dropped due to unavailablity of pool */ struct ol_txrx_pool_stats { uint16_t pool_map_count; uint16_t pool_unmap_count; + uint16_t pool_resize_count; uint16_t pkt_drop_no_pool; }; @@ -472,6 +474,7 @@ struct ol_txrx_pool_stats { * @flow_pool_size: flow_pool size * @avail_desc: available descriptors * @deficient_desc: deficient descriptors + * @overflow_desc: overflow descriptors * @status: flow pool status * @flow_type: flow pool type * @member_flow_id: member flow id @@ -490,6 +493,7 @@ struct ol_tx_flow_pool_t { uint16_t flow_pool_size; uint16_t avail_desc; uint16_t deficient_desc; + uint16_t overflow_desc; enum flow_pool_status status; enum htt_flow_type flow_type; uint8_t member_flow_id;