Browse Source

qcacld-3.0: Add capability to disable checksum for legacy links

Due to latency concerns on GEN1 PCIe attach checksum offload and TSO
features cannot be enabled for 802.11A/B/G legacy mode connections.
So add INI option to dynamically disable/enable checksum offload and TSO
based on the connection type.
Use netdev_update_features kernel API to adjust the netdev features
dynamically for existing registered netdev.

change-id: I0e44cb1789516e1bcee619be3581d826f45f265b
CRs-Fixed: 2732090
Manjunathappa Prakash 4 years ago
parent
commit
ef666adfcf

+ 2 - 0
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);

+ 51 - 13
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) */

+ 2 - 0
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);

+ 15 - 0
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)

+ 9 - 0
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

+ 166 - 0
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 <cdp_txrx_ctrl.h>
 
 #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,