diff --git a/core/hdd/inc/wlan_hdd_main.h b/core/hdd/inc/wlan_hdd_main.h index 286bd9725ac0..216a758cfdc9 100644 --- a/core/hdd/inc/wlan_hdd_main.h +++ b/core/hdd/inc/wlan_hdd_main.h @@ -1118,6 +1118,8 @@ struct hdd_context; * @gro_flushed: Flag to indicate if GRO explicit flush is done or not * @handle_feature_update: Handle feature update only if it is triggered * by hdd_netdev_feature_update + * @netdev_features_update_work: work for handling the netdev features update + for the adapter. */ struct hdd_adapter { /* Magic cookie for adapter sanity verification. Note that this @@ -1415,6 +1417,8 @@ struct hdd_adapter { uint8_t gro_disallowed[DP_MAX_RX_THREADS]; uint8_t gro_flushed[DP_MAX_RX_THREADS]; bool handle_feature_update; + + qdf_work_t netdev_features_update_work; }; #define WLAN_HDD_GET_STATION_CTX_PTR(adapter) (&(adapter)->session.station) @@ -1690,6 +1694,7 @@ struct hdd_fw_ver_info { * @current_pcie_gen_speed: current pcie gen speed * @pm_qos_req: pm_qos request for all cpu cores * @qos_cpu_mask: voted cpu core mask + * @adapter_ops_wq: High priority workqueue for handling adapter operations */ struct hdd_context { struct wlan_objmgr_psoc *psoc; @@ -2021,6 +2026,7 @@ struct hdd_context { uint8_t gro_force_flush[DP_MAX_RX_THREADS]; } dp_agg_param; int current_pcie_gen_speed; + qdf_workqueue_t *adapter_ops_wq; }; /** @@ -2814,6 +2820,41 @@ hdd_is_low_tput_gro_enable(struct hdd_context *hdd_ctx) #endif /*WLAN_FEATURE_DP_BUS_BANDWIDTH*/ +/** + * hdd_init_adapter_ops_wq() - Init global workqueue for adapter operations. + * @hdd_ctx: pointer to HDD context + * + * Return: QDF_STATUS_SUCCESS if workqueue is allocated, + * QDF_STATUS_E_NOMEM if workqueue aloocation fails. + */ +QDF_STATUS hdd_init_adapter_ops_wq(struct hdd_context *hdd_ctx); + +/** + * hdd_deinit_adapter_ops_wq() - Deinit global workqueue for adapter operations. + * @hdd_ctx: pointer to HDD context + * + * Return: None + */ +void hdd_deinit_adapter_ops_wq(struct hdd_context *hdd_ctx); + +/** + * hdd_adapter_feature_update_work_init() - Init per adapter work for netdev + * feature update + * @adapter: pointer to adapter structure + * + * Return: QDF_STATUS + */ +QDF_STATUS hdd_adapter_feature_update_work_init(struct hdd_adapter *adapter); + +/** + * hdd_adapter_feature_update_work_deinit() - Deinit per adapter work for + * netdev feature update + * @adapter: pointer to adapter structure + * + * Return: QDF_STATUS + */ +void hdd_adapter_feature_update_work_deinit(struct hdd_adapter *adapter); + int hdd_qdf_trace_enable(QDF_MODULE_ID module_id, uint32_t bitmask); int hdd_init(void); diff --git a/core/hdd/src/wlan_hdd_assoc.c b/core/hdd/src/wlan_hdd_assoc.c index 913934bd7f4a..d551b945dae9 100644 --- a/core/hdd/src/wlan_hdd_assoc.c +++ b/core/hdd/src/wlan_hdd_assoc.c @@ -1599,8 +1599,6 @@ static void hdd_send_association_event(struct net_device *dev, if (ucfg_pkt_capture_get_pktcap_mode(hdd_ctx->psoc)) ucfg_pkt_capture_record_channel(adapter->vdev); - - hdd_netdev_update_features(adapter); } else { /* Not Associated */ hdd_nofl_info("%s(vdevid-%d): disconnected", dev->name, adapter->vdev_id); @@ -2734,6 +2732,33 @@ void hdd_clear_fils_connection_info(struct hdd_adapter *adapter) } #endif +/** + * hdd_netif_queue_enable() - Enable the network queue for a + * particular adapter. + * @adapter: pointer to the adapter structure + * + * This function schedules a work to update the netdev features + * and enable the network queue if the feature "disable checksum/tso + * for legacy connections" is enabled via INI. If not, it will + * retain the existing behavior by just enabling the network queues. + * + * Returns: none + */ +static inline void hdd_netif_queue_enable(struct hdd_adapter *adapter) +{ + ol_txrx_soc_handle soc = cds_get_context(QDF_MODULE_ID_SOC); + struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + + if (cdp_cfg_get(soc, cfg_dp_disable_legacy_mode_csum_offload)) { + qdf_queue_work(0, hdd_ctx->adapter_ops_wq, + &adapter->netdev_features_update_work); + } else { + wlan_hdd_netif_queue_control(adapter, + WLAN_WAKE_ALL_NETIF_QUEUE, + WLAN_CONTROL_PATH); + } +} + /** * hdd_association_completion_handler() - association completion handler * @adapter: pointer to adapter @@ -3222,10 +3247,7 @@ hdd_association_completion_handler(struct hdd_adapter *adapter, roam_info, roam_info->bss_desc); hdd_debug("Enabling queues"); - wlan_hdd_netif_queue_control(adapter, - WLAN_WAKE_ALL_NETIF_QUEUE, - WLAN_CONTROL_PATH); - + hdd_netif_queue_enable(adapter); } qdf_mem_free(rsp_rsn_ie); } else { @@ -3279,9 +3301,7 @@ hdd_association_completion_handler(struct hdd_adapter *adapter, /* Start the tx queues */ hdd_debug("Enabling queues"); - wlan_hdd_netif_queue_control(adapter, - WLAN_WAKE_ALL_NETIF_QUEUE, - WLAN_CONTROL_PATH); + hdd_netif_queue_enable(adapter); } qdf_mem_free(reqRsnIe); diff --git a/core/hdd/src/wlan_hdd_main.c b/core/hdd/src/wlan_hdd_main.c index 39948bf48308..8a785f79928c 100644 --- a/core/hdd/src/wlan_hdd_main.c +++ b/core/hdd/src/wlan_hdd_main.c @@ -3775,6 +3775,7 @@ int hdd_wlan_start_modules(struct hdd_context *hdd_ctx, bool reinit) } hdd_bus_bandwidth_init(hdd_ctx); + hdd_init_adapter_ops_wq(hdd_ctx); pld_set_fw_log_mode(hdd_ctx->parent_dev, hdd_ctx->config->enable_fw_log); ret = hdd_hif_open(qdf_dev->dev, qdf_dev->drv_hdl, qdf_dev->bid, @@ -4005,6 +4006,7 @@ hif_close: hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); hdd_hif_close(hdd_ctx, hif_ctx); power_down: + hdd_deinit_adapter_ops_wq(hdd_ctx); hdd_bus_bandwidth_deinit(hdd_ctx); if (!reinit && !unint) pld_power_off(qdf_dev->dev); @@ -5051,6 +5053,10 @@ hdd_alloc_station_adapter(struct hdd_context *hdd_ctx, tSirMacAddr mac_addr, goto free_net_dev; } + qdf_status = hdd_adapter_feature_update_work_init(adapter); + if (QDF_IS_STATUS_ERROR(qdf_status)) + goto free_net_dev; + init_completion(&adapter->vdev_destroy_event); adapter->offloads_configured = false; @@ -5831,6 +5837,7 @@ static void hdd_cleanup_adapter(struct hdd_context *hdd_ctx, wlan_hdd_debugfs_csr_deinit(adapter); hdd_debugfs_exit(adapter); + hdd_adapter_feature_update_work_deinit(adapter); /* * The adapter is marked as closed. When hdd_wlan_exit() call returns, @@ -10029,6 +10036,117 @@ void hdd_bus_bandwidth_deinit(struct hdd_context *hdd_ctx) } #endif /*WLAN_FEATURE_DP_BUS_BANDWIDTH*/ +/** + * __hdd_adapter_param_update_work() - Gist of the work to process + * netdev feature update. + * @adapter: pointer to adapter structure + * + * This function assumes that the adapter pointer is always valid. + * So the caller shoudl always validate adapter pointer before calling + * this function + * + * Returns: None + */ +static inline void +__hdd_adapter_param_update_work(struct hdd_adapter *adapter) +{ + /** + * This check is needed in case the work got scheduled after the + * interface got disconnected. During disconnection, the network queues + * are paused and hence should not be, mistakenly, restarted here. + * There are two approaches to handle this case + * 1) Flush the work during disconnection + * 2) Check for connected state in work + * + * Since the flushing of work during disconnection will need to be + * done at multiple places or entry points, instead its preferred to + * check the connection state and skip the operation here. + */ + if (!hdd_adapter_is_connected_sta(adapter)) + return; + + hdd_netdev_update_features(adapter); + + hdd_debug("Enabling queues"); + wlan_hdd_netif_queue_control(adapter, WLAN_WAKE_ALL_NETIF_QUEUE, + WLAN_CONTROL_PATH); +} + +/** + * hdd_adapter_param_update_work() - work to process the netdev features + * update. + * @arg: private data passed to work + * + * Returns: None + */ +static void hdd_adapter_param_update_work(void *arg) +{ + struct hdd_adapter *adapter = arg; + struct osif_vdev_sync *vdev_sync; + int errno; + + if (hdd_validate_adapter(adapter)) { + hdd_err("netdev features update request for invalid adapter"); + return; + } + + errno = osif_vdev_sync_op_start(adapter->dev, &vdev_sync); + if (errno) + return; + + __hdd_adapter_param_update_work(adapter); + + osif_vdev_sync_op_stop(vdev_sync); +} + +QDF_STATUS hdd_init_adapter_ops_wq(struct hdd_context *hdd_ctx) +{ + hdd_enter(); + + hdd_ctx->adapter_ops_wq = + qdf_alloc_high_prior_ordered_workqueue("hdd_adapter_ops_wq"); + if (!hdd_ctx->adapter_ops_wq) + return QDF_STATUS_E_NOMEM; + + hdd_exit(); + + return QDF_STATUS_SUCCESS; +} + +void hdd_deinit_adapter_ops_wq(struct hdd_context *hdd_ctx) +{ + hdd_enter(); + + qdf_flush_workqueue(0, hdd_ctx->adapter_ops_wq); + qdf_destroy_workqueue(0, hdd_ctx->adapter_ops_wq); + + hdd_exit(); +} + +QDF_STATUS hdd_adapter_feature_update_work_init(struct hdd_adapter *adapter) +{ + QDF_STATUS status; + + hdd_enter(); + + status = qdf_create_work(0, &adapter->netdev_features_update_work, + hdd_adapter_param_update_work, adapter); + + hdd_exit(); + + return status; +} + +void hdd_adapter_feature_update_work_deinit(struct hdd_adapter *adapter) +{ + hdd_enter(); + + qdf_cancel_work(&adapter->netdev_features_update_work); + qdf_flush_work(&adapter->netdev_features_update_work); + + hdd_exit(); +} + static uint8_t *convert_level_to_string(uint32_t level) { switch (level) { @@ -13805,6 +13923,7 @@ int hdd_wlan_stop_modules(struct hdd_context *hdd_ctx, bool ftm_mode) hdd_sap_destroy_ctx_all(hdd_ctx, is_recovery_stop); hdd_sta_destroy_ctx_all(hdd_ctx); pld_request_bus_bandwidth(hdd_ctx->parent_dev, PLD_BUS_WIDTH_NONE); + hdd_deinit_adapter_ops_wq(hdd_ctx); hdd_bus_bandwidth_deinit(hdd_ctx); hdd_check_for_leaks(hdd_ctx, is_recovery_stop); hdd_debug_domain_set(QDF_DEBUG_DOMAIN_INIT);