diff --git a/core/hdd/inc/wlan_hdd_main.h b/core/hdd/inc/wlan_hdd_main.h index 625d1835b5ea..baae4df4cd68 100644 --- a/core/hdd/inc/wlan_hdd_main.h +++ b/core/hdd/inc/wlan_hdd_main.h @@ -1340,12 +1340,16 @@ struct hdd_adapter { uint64_t prev_fwd_rx_packets; int connection; #endif -#ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL +#if defined(QCA_LL_LEGACY_TX_FLOW_CONTROL) || \ + defined(QCA_HL_NETDEV_FLOW_CONTROL) qdf_mc_timer_t tx_flow_control_timer; bool tx_flow_timer_initialized; +#endif /* QCA_LL_LEGACY_TX_FLOW_CONTROL || QCA_HL_NETDEV_FLOW_CONTROL */ +#ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL unsigned int tx_flow_low_watermark; unsigned int tx_flow_high_watermark_offset; #endif /* QCA_LL_LEGACY_TX_FLOW_CONTROL */ + bool offloads_configured; /* DSCP to UP QoS Mapping */ @@ -2469,6 +2473,16 @@ void hdd_clean_up_pre_cac_interface(struct hdd_context *hdd_ctx); void wlan_hdd_txrx_pause_cb(uint8_t vdev_id, enum netif_action_type action, enum netif_reason_type reason); +#ifdef QCA_HL_NETDEV_FLOW_CONTROL +void wlan_hdd_mod_fc_timer(struct hdd_adapter *adapter, + enum netif_action_type action); +#else +static inline void wlan_hdd_mod_fc_timer(struct hdd_adapter *adapter, + enum netif_action_type action) +{ +} +#endif /* QCA_HL_NETDEV_FLOW_CONTROL */ + int hdd_wlan_dump_stats(struct hdd_adapter *adapter, int value); void wlan_hdd_deinit_tx_rx_histogram(struct hdd_context *hdd_ctx); void wlan_hdd_display_tx_rx_histogram(struct hdd_context *hdd_ctx); diff --git a/core/hdd/inc/wlan_hdd_tx_rx.h b/core/hdd/inc/wlan_hdd_tx_rx.h index 193cb158f1ed..9b156a7d22c4 100644 --- a/core/hdd/inc/wlan_hdd_tx_rx.h +++ b/core/hdd/inc/wlan_hdd_tx_rx.h @@ -129,7 +129,6 @@ void hdd_tx_resume_cb(void *adapter_context, bool tx_resume); * Return: true if TX Q is paused by flow control */ bool hdd_tx_flow_control_is_pause(void *adapter_context); -void hdd_tx_resume_timer_expired_handler(void *adapter_context); /** * hdd_register_tx_flow_control() - Register TX Flow control @@ -156,9 +155,6 @@ static inline bool hdd_tx_flow_control_is_pause(void *adapter_context) { return false; } -static inline void hdd_tx_resume_timer_expired_handler(void *adapter_context) -{ -} static inline void hdd_register_tx_flow_control(struct hdd_adapter *adapter, qdf_mc_timer_callback_t timer_callback, ol_txrx_tx_flow_control_fp flowControl, @@ -174,6 +170,30 @@ static inline void hdd_get_tx_resource(struct hdd_adapter *adapter, } #endif /* QCA_LL_LEGACY_TX_FLOW_CONTROL */ +#if defined(QCA_LL_LEGACY_TX_FLOW_CONTROL) || \ + defined(QCA_HL_NETDEV_FLOW_CONTROL) +void hdd_tx_resume_timer_expired_handler(void *adapter_context); +#else +static inline void hdd_tx_resume_timer_expired_handler(void *adapter_context) +{ +} +#endif + +#ifdef QCA_HL_NETDEV_FLOW_CONTROL +void hdd_register_hl_netdev_fc_timer(struct hdd_adapter *adapter, + qdf_mc_timer_callback_t timer_callback); +void hdd_deregister_hl_netdev_fc_timer(struct hdd_adapter *adapter); +#else +static inline void hdd_register_hl_netdev_fc_timer(struct hdd_adapter *adapter, + qdf_mc_timer_callback_t + timer_callback) +{} + +static inline void + hdd_deregister_hl_netdev_fc_timer(struct hdd_adapter *adapter) +{} +#endif /* QCA_HL_NETDEV_FLOW_CONTROL */ + int hdd_get_peer_idx(struct hdd_station_ctx *sta_ctx, struct qdf_mac_addr *addr); diff --git a/core/hdd/src/wlan_hdd_main.c b/core/hdd/src/wlan_hdd_main.c index dac0b32a99ff..e4a31265d5d4 100644 --- a/core/hdd/src/wlan_hdd_main.c +++ b/core/hdd/src/wlan_hdd_main.c @@ -321,6 +321,32 @@ static void hdd_set_rps_cpu_mask(struct hdd_context *hdd_ctx) hdd_send_rps_ind(adapter); } +#ifdef QCA_HL_NETDEV_FLOW_CONTROL +void wlan_hdd_mod_fc_timer(struct hdd_adapter *adapter, + enum netif_action_type action) +{ + if (!adapter->tx_flow_timer_initialized) + return; + + if (action == WLAN_WAKE_NON_PRIORITY_QUEUE) { + qdf_mc_timer_stop(&adapter->tx_flow_control_timer); + } else if (action == WLAN_STOP_NON_PRIORITY_QUEUE) { + QDF_STATUS status = + qdf_mc_timer_start(&adapter->tx_flow_control_timer, + WLAN_HDD_TX_FLOW_CONTROL_OS_Q_BLOCK_TIME); + + if (!QDF_IS_STATUS_SUCCESS(status)) + hdd_err("Failed to start tx_flow_control_timer"); + else + adapter-> + hdd_stats.tx_rx_stats.txflow_timer_cnt++; + + adapter->hdd_stats.tx_rx_stats.txflow_pause_cnt++; + adapter->hdd_stats.tx_rx_stats.is_txflow_paused = true; + } +} +#endif /* QCA_HL_NETDEV_FLOW_CONTROL */ + /** * wlan_hdd_txrx_pause_cb() - pause callback from txrx layer * @vdev_id: vdev_id @@ -340,7 +366,7 @@ void wlan_hdd_txrx_pause_cb(uint8_t vdev_id, return; } adapter = hdd_get_adapter_by_vdev(hdd_ctx, vdev_id); - + wlan_hdd_mod_fc_timer(adapter, action); wlan_hdd_netif_queue_control(adapter, action, reason); } @@ -4807,6 +4833,9 @@ struct hdd_adapter *hdd_open_adapter(struct hdd_context *hdd_ctx, uint8_t sessio hdd_err("Interface %s wow debug_fs init failed", netdev_name(adapter->dev)); + hdd_register_hl_netdev_fc_timer(adapter, + hdd_tx_resume_timer_expired_handler); + hdd_info("%s interface created. iftype: %d", netdev_name(adapter->dev), session_type); @@ -5200,6 +5229,8 @@ QDF_STATUS hdd_stop_adapter_ext(struct hdd_context *hdd_ctx, break; } + hdd_deregister_hl_netdev_fc_timer(adapter); + if (adapter->scan_info.default_scan_ies) { qdf_mem_free(adapter->scan_info.default_scan_ies); adapter->scan_info.default_scan_ies = NULL; diff --git a/core/hdd/src/wlan_hdd_tx_rx.c b/core/hdd/src/wlan_hdd_tx_rx.c index cfa3c66e4eb2..1300b1094fa9 100644 --- a/core/hdd/src/wlan_hdd_tx_rx.c +++ b/core/hdd/src/wlan_hdd_tx_rx.c @@ -85,6 +85,57 @@ const uint8_t hdd_qdisc_ac_to_tl_ac[] = { #endif +#ifdef QCA_HL_NETDEV_FLOW_CONTROL +void hdd_register_hl_netdev_fc_timer(struct hdd_adapter *adapter, + qdf_mc_timer_callback_t timer_callback) +{ + if (!adapter->tx_flow_timer_initialized) { + qdf_mc_timer_init(&adapter->tx_flow_control_timer, + QDF_TIMER_TYPE_SW, timer_callback, adapter); + adapter->tx_flow_timer_initialized = true; + } +} + +/** + * hdd_deregister_hl_netdev_fc_timer() - Deregister HL Flow Control Timer + * @adapter: adapter handle + * + * Return: none + */ +void hdd_deregister_hl_netdev_fc_timer(struct hdd_adapter *adapter) +{ + if (adapter->tx_flow_timer_initialized) { + qdf_mc_timer_stop(&adapter->tx_flow_control_timer); + qdf_mc_timer_destroy(&adapter->tx_flow_control_timer); + adapter->tx_flow_timer_initialized = false; + } +} + +/** + * hdd_tx_resume_timer_expired_handler() - TX Q resume timer handler + * @adapter_context: pointer to vdev adapter + * + * If Blocked OS Q is not resumed during timeout period, to prevent + * permanent stall, resume OS Q forcefully. + * + * Return: None + */ +void hdd_tx_resume_timer_expired_handler(void *adapter_context) +{ + struct hdd_adapter *adapter = (struct hdd_adapter *)adapter_context; + + if (!adapter) { + /* INVALID ARG */ + return; + } + + hdd_debug("Enabling queues"); + wlan_hdd_netif_queue_control(adapter, WLAN_WAKE_NON_PRIORITY_QUEUE, + WLAN_DATA_FLOW_CONTROL); +} + +#endif /* QCA_HL_NETDEV_FLOW_CONTROL */ + #ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL /** * hdd_tx_resume_timer_expired_handler() - TX Q resume timer handler