diff --git a/core/cds/src/cds_api.c b/core/cds/src/cds_api.c index d08a593b89bc..0ae4678c41e1 100644 --- a/core/cds/src/cds_api.c +++ b/core/cds/src/cds_api.c @@ -427,6 +427,8 @@ static void cds_cdp_cfg_attach(struct wlan_objmgr_psoc *psoc) cfg_get(psoc, CFG_DP_NAN_TCP_UDP_CKSUM_OFFLOAD); cdp_cfg.p2p_ip_tcp_udp_checksum_offload = cfg_get(psoc, CFG_DP_P2P_TCP_UDP_CKSUM_OFFLOAD); + cdp_cfg.legacy_mode_csum_disable = + cfg_get(psoc, CFG_DP_LEGACY_MODE_CSUM_DISABLE); cdp_cfg.ce_classify_enabled = cfg_get(psoc, CFG_DP_CE_CLASSIFY_ENABLE); cdp_cfg.tso_enable = cfg_get(psoc, CFG_DP_TSO); diff --git a/core/hdd/inc/wlan_hdd_main.h b/core/hdd/inc/wlan_hdd_main.h index 646cdb06d213..286bd9725ac0 100644 --- a/core/hdd/inc/wlan_hdd_main.h +++ b/core/hdd/inc/wlan_hdd_main.h @@ -1116,6 +1116,8 @@ struct hdd_context; any UDP packet. * @gro_disallowed: Flag to check if GRO is enabled or disable for adapter * @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 */ struct hdd_adapter { /* Magic cookie for adapter sanity verification. Note that this @@ -1412,6 +1414,7 @@ struct hdd_adapter { #endif uint8_t gro_disallowed[DP_MAX_RX_THREADS]; uint8_t gro_flushed[DP_MAX_RX_THREADS]; + bool handle_feature_update; }; #define WLAN_HDD_GET_STATION_CTX_PTR(adapter) (&(adapter)->session.station) @@ -3304,6 +3307,36 @@ static inline void hdd_set_sg_flags(struct hdd_context *hdd_ctx, void hdd_set_netdev_flags(struct hdd_adapter *adapter); #ifdef FEATURE_TSO +/** + * hdd_get_tso_csum_feature_flags() - Return TSO and csum flags if enabled + * + * Return: Enabled feature flags set, 0 on failure + */ +static inline netdev_features_t hdd_get_tso_csum_feature_flags(void) +{ + netdev_features_t netdev_features = 0; + ol_txrx_soc_handle soc = cds_get_context(QDF_MODULE_ID_SOC); + + if (!soc) { + hdd_err("soc handle is NULL"); + return 0; + } + + if (cdp_cfg_get(soc, cfg_dp_enable_ip_tcp_udp_checksum_offload)) { + netdev_features = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; + + if (cdp_cfg_get(soc, cfg_dp_tso_enable)) { + /* + * Enable TSO only if IP/UDP/TCP TX checksum flag is + * enabled. + */ + netdev_features |= NETIF_F_TSO | NETIF_F_TSO6 | + NETIF_F_SG; + } + } + return netdev_features; +} + /** * hdd_set_tso_flags() - enable TSO flags in the network device * @hdd_ctx: HDD context @@ -3317,19 +3350,9 @@ void hdd_set_netdev_flags(struct hdd_adapter *adapter); static inline void hdd_set_tso_flags(struct hdd_context *hdd_ctx, struct net_device *wlan_dev) { - if (cdp_cfg_get(cds_get_context(QDF_MODULE_ID_SOC), - cfg_dp_tso_enable) && - cdp_cfg_get(cds_get_context(QDF_MODULE_ID_SOC), - cfg_dp_enable_ip_tcp_udp_checksum_offload)){ - /* - * We want to enable TSO only if IP/UDP/TCP TX checksum flag is - * enabled. - */ - hdd_debug("TSO Enabled"); - wlan_dev->features |= - NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | - NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_SG; - } + hdd_debug("TSO Enabled"); + + wlan_dev->features |= hdd_get_tso_csum_feature_flags(); } #else static inline void hdd_set_tso_flags(struct hdd_context *hdd_ctx, @@ -3337,6 +3360,11 @@ static inline void hdd_set_tso_flags(struct hdd_context *hdd_ctx, { hdd_set_sg_flags(hdd_ctx, wlan_dev); } + +static inline netdev_features_t hdd_get_tso_csum_feature_flags(void) +{ + return 0; +} #endif /* FEATURE_TSO */ /** @@ -4517,4 +4545,14 @@ static inline void hdd_beacon_latency_event_cb(uint32_t latency_level) } #endif +/** + * hdd_netdev_feature_update - Update the netdev features + * @net_dev: Handle to net_device + * + * This func holds the rtnl_lock. Do not call with rtnl_lock held. + * + * Return: None + */ +void hdd_netdev_update_features(struct hdd_adapter *adapter); + #endif /* end #if !defined(WLAN_HDD_MAIN_H) */ diff --git a/core/hdd/src/wlan_hdd_assoc.c b/core/hdd/src/wlan_hdd_assoc.c index cc7b2355a1aa..913934bd7f4a 100644 --- a/core/hdd/src/wlan_hdd_assoc.c +++ b/core/hdd/src/wlan_hdd_assoc.c @@ -1599,6 +1599,8 @@ 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); diff --git a/core/hdd/src/wlan_hdd_cfg80211.c b/core/hdd/src/wlan_hdd_cfg80211.c index ecf54f2525ef..959758bc41d7 100644 --- a/core/hdd/src/wlan_hdd_cfg80211.c +++ b/core/hdd/src/wlan_hdd_cfg80211.c @@ -24011,6 +24011,21 @@ hdd_convert_cfgdot11mode_to_80211mode(enum csr_cfgdot11mode mode) } } +bool hdd_is_legacy_connection(struct hdd_adapter *adapter) +{ + struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + int connection_mode; + + connection_mode = hdd_convert_cfgdot11mode_to_80211mode( + sta_ctx->conn_info.dot11mode); + if (connection_mode == QCA_WLAN_802_11_MODE_11A || + connection_mode == QCA_WLAN_802_11_MODE_11B || + connection_mode == QCA_WLAN_802_11_MODE_11G) + return true; + else + return false; +} + static int __wlan_hdd_cfg80211_get_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct cfg80211_chan_def *chandef) diff --git a/core/hdd/src/wlan_hdd_cfg80211.h b/core/hdd/src/wlan_hdd_cfg80211.h index 0b0e337d265a..ae2ea6cece37 100644 --- a/core/hdd/src/wlan_hdd_cfg80211.h +++ b/core/hdd/src/wlan_hdd_cfg80211.h @@ -851,4 +851,13 @@ static inline void hdd_send_update_owe_info_event(struct hdd_adapter *adapter, { } #endif + +/** + * hdd_is_legacy_connection() - Is adapter connection is legacy + * @adapter: Handle to hdd_adapter + * + * Return: true if connection mode is legacy, false otherwise. + */ +bool hdd_is_legacy_connection(struct hdd_adapter *adapter); + #endif diff --git a/core/hdd/src/wlan_hdd_main.c b/core/hdd/src/wlan_hdd_main.c index 3f6b4a642e99..39948bf48308 100644 --- a/core/hdd/src/wlan_hdd_main.c +++ b/core/hdd/src/wlan_hdd_main.c @@ -192,6 +192,7 @@ #include "wlan_coex_ucfg_api.h" #include "wlan_cm_roam_api.h" #include "wlan_cm_roam_ucfg_api.h" +#include #ifdef MODULE #define WLAN_MODULE_NAME module_name(THIS_MODULE) @@ -1026,6 +1027,9 @@ static int __hdd_netdev_notifier_call(struct net_device *net_dev, */ wlan_cfg80211_cleanup_scan_queue(hdd_ctx->pdev, net_dev); break; + case NETDEV_FEAT_CHANGE: + hdd_debug("vdev %d netdev Feature 0x%llx\n", + adapter->vdev_id, net_dev->features); default: break; @@ -4748,11 +4752,173 @@ static const struct ethtool_ops wlan_ethtool_ops = { }; #endif +/** + * __hdd_fix_features - Adjust the feature flags needed to be updated + * @net_dev: Handle to net_device + * @features: Currently enabled feature flags + * + * Return: Adjusted feature flags on success, old feature on failure + */ +static netdev_features_t __hdd_fix_features(struct net_device *net_dev, + netdev_features_t features) +{ + netdev_features_t feature_change_req = features; + netdev_features_t feature_tso_csum; + struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(net_dev); + + if (!adapter->handle_feature_update) { + hdd_debug("Not triggered by hdd_netdev_update_features"); + return features; + } + + feature_tso_csum = hdd_get_tso_csum_feature_flags(); + if (hdd_is_legacy_connection(adapter)) + /* Disable checksum and TSO */ + feature_change_req &= ~feature_tso_csum; + else + /* Enable checksum and TSO */ + feature_change_req |= feature_tso_csum; + + hdd_debug("vdev mode %d current features 0x%llx, requesting feature change 0x%llx", + adapter->device_mode, net_dev->features, + feature_change_req); + + return feature_change_req; +} + +/** + * hdd_fix_features() - Wrapper for __hdd_fix_features to protect it from SSR + * @net_dev: Pointer to net_device structure + * @features: Updated features set + * + * Adjusts the feature request, do not update the device yet. + * + * Return: updated feature for success, incoming feature as is on failure + */ +static netdev_features_t hdd_fix_features(struct net_device *net_dev, + netdev_features_t features) +{ + int errno; + int changed_features = features; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(net_dev, &vdev_sync); + if (errno) + return features; + + changed_features = __hdd_fix_features(net_dev, features); + + osif_vdev_sync_op_stop(vdev_sync); + + return changed_features; +} +/** + * __hdd_set_features - Update device config for resultant change in feature + * @net_dev: Handle to net_device + * @features: Existing + requested feature after resolving the dependency + * + * Return: 0 on success, non zero error on failure + */ +static int __hdd_set_features(struct net_device *net_dev, + netdev_features_t features) +{ + struct hdd_adapter *adapter = netdev_priv(net_dev); + cdp_config_param_type vdev_param; + ol_txrx_soc_handle soc = cds_get_context(QDF_MODULE_ID_SOC); + + if (!adapter->handle_feature_update) { + hdd_debug("Not triggered by hdd_netdev_update_features"); + return 0; + } + + if (!soc) { + hdd_err("soc handle is NULL"); + return 0; + } + + hdd_debug("vdev mode %d vdev_id %d current features 0x%llx, changed features 0x%llx", + adapter->device_mode, adapter->vdev_id, net_dev->features, + features); + + if (features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) + vdev_param.cdp_enable_tx_checksum = true; + else + vdev_param.cdp_enable_tx_checksum = false; + + if (cdp_txrx_set_vdev_param(soc, adapter->vdev_id, CDP_ENABLE_CSUM, + vdev_param)) + hdd_debug("Failed to set DP vdev params"); + + return 0; +} + +/** + * hdd_set_features() - Wrapper for __hdd_set_features to protect it from SSR + * @net_dev: Pointer to net_device structure + * @features: Updated features set + * + * Is called to update device configurations for changed features. + * + * Return: 0 for success, non-zero for failure + */ +static int hdd_set_features(struct net_device *net_dev, + netdev_features_t features) +{ + int errno; + struct osif_vdev_sync *vdev_sync; + + errno = osif_vdev_sync_op_start(net_dev, &vdev_sync); + if (errno) + return errno; + + errno = __hdd_set_features(net_dev, features); + + osif_vdev_sync_op_stop(vdev_sync); + + return errno; +} + +void hdd_netdev_update_features(struct hdd_adapter *adapter) +{ + struct net_device *net_dev = adapter->dev; + ol_txrx_soc_handle soc = cds_get_context(QDF_MODULE_ID_SOC); + bool request_feature_update = false; + + if (!soc) { + hdd_err("soc handle is NULL"); + return; + } + + if (!cdp_cfg_get(soc, cfg_dp_disable_legacy_mode_csum_offload)) + return; + + switch (adapter->device_mode) { + case QDF_STA_MODE: + if (cdp_cfg_get(soc, cfg_dp_enable_ip_tcp_udp_checksum_offload)) + request_feature_update = true; + break; + default: + break; + } + + if (request_feature_update) { + hdd_debug("Update net_dev features for device mode %d", + adapter->device_mode); + rtnl_lock(); + adapter->handle_feature_update = true; + netdev_update_features(net_dev); + adapter->handle_feature_update = false; + rtnl_unlock(); + } +} + static const struct net_device_ops wlan_drv_ops = { .ndo_open = hdd_open, .ndo_stop = hdd_stop, .ndo_uninit = hdd_uninit, .ndo_start_xmit = hdd_hard_start_xmit, + .ndo_fix_features = hdd_fix_features, + .ndo_set_features = hdd_set_features, .ndo_tx_timeout = hdd_tx_timeout, .ndo_get_stats = hdd_get_stats, .ndo_do_ioctl = hdd_ioctl,