Commit ac8c97b1 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'gve-add-rss-config-support'

Ziwei Xiao says:

====================
gve: Add RSS config support

These two patches are used to add RSS config support in GVE driver
between the device and ethtool.

v2: https://lore.kernel.org/20240808205530.726871-1-pkaligineedi@google.com
====================

Link: https://patch.msgid.link/20240812222013.1503584-1-pkaligineedi@google.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents be034ee6 fa46c456
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -784,6 +784,8 @@ struct gve_priv {
	u32 adminq_verify_driver_compatibility_cnt;
	u32 adminq_query_flow_rules_cnt;
	u32 adminq_cfg_flow_rule_cnt;
	u32 adminq_cfg_rss_cnt;
	u32 adminq_query_rss_cnt;

	/* Global stats */
	u32 interface_up_cnt; /* count of times interface turned up since last reset */
@@ -831,6 +833,9 @@ struct gve_priv {
	u32 num_flow_rules;

	struct gve_flow_rules_cache flow_rules_cache;

	u16 rss_key_size;
	u16 rss_lut_size;
};

enum gve_service_task_flags_bit {
+180 −2
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ void gve_parse_device_option(struct gve_priv *priv,
			     struct gve_device_option_dqo_qpl **dev_op_dqo_qpl,
			     struct gve_device_option_buffer_sizes **dev_op_buffer_sizes,
			     struct gve_device_option_flow_steering **dev_op_flow_steering,
			     struct gve_device_option_rss_config **dev_op_rss_config,
			     struct gve_device_option_modify_ring **dev_op_modify_ring)
{
	u32 req_feat_mask = be32_to_cpu(option->required_features_mask);
@@ -207,6 +208,23 @@ void gve_parse_device_option(struct gve_priv *priv,
				 "Flow Steering");
		*dev_op_flow_steering = (void *)(option + 1);
		break;
	case GVE_DEV_OPT_ID_RSS_CONFIG:
		if (option_length < sizeof(**dev_op_rss_config) ||
		    req_feat_mask != GVE_DEV_OPT_REQ_FEAT_MASK_RSS_CONFIG) {
			dev_warn(&priv->pdev->dev, GVE_DEVICE_OPTION_ERROR_FMT,
				 "RSS config",
				 (int)sizeof(**dev_op_rss_config),
				 GVE_DEV_OPT_REQ_FEAT_MASK_RSS_CONFIG,
				 option_length, req_feat_mask);
			break;
		}

		if (option_length > sizeof(**dev_op_rss_config))
			dev_warn(&priv->pdev->dev,
				 GVE_DEVICE_OPTION_TOO_BIG_FMT,
				 "RSS config");
		*dev_op_rss_config = (void *)(option + 1);
		break;
	default:
		/* If we don't recognize the option just continue
		 * without doing anything.
@@ -227,6 +245,7 @@ gve_process_device_options(struct gve_priv *priv,
			   struct gve_device_option_dqo_qpl **dev_op_dqo_qpl,
			   struct gve_device_option_buffer_sizes **dev_op_buffer_sizes,
			   struct gve_device_option_flow_steering **dev_op_flow_steering,
			   struct gve_device_option_rss_config **dev_op_rss_config,
			   struct gve_device_option_modify_ring **dev_op_modify_ring)
{
	const int num_options = be16_to_cpu(descriptor->num_device_options);
@@ -249,7 +268,8 @@ gve_process_device_options(struct gve_priv *priv,
					dev_op_gqi_rda, dev_op_gqi_qpl,
					dev_op_dqo_rda, dev_op_jumbo_frames,
					dev_op_dqo_qpl, dev_op_buffer_sizes,
					dev_op_flow_steering, dev_op_modify_ring);
					dev_op_flow_steering, dev_op_rss_config,
					dev_op_modify_ring);
		dev_opt = next_opt;
	}

@@ -289,6 +309,8 @@ int gve_adminq_alloc(struct device *dev, struct gve_priv *priv)
	priv->adminq_get_ptype_map_cnt = 0;
	priv->adminq_query_flow_rules_cnt = 0;
	priv->adminq_cfg_flow_rule_cnt = 0;
	priv->adminq_cfg_rss_cnt = 0;
	priv->adminq_query_rss_cnt = 0;

	/* Setup Admin queue with the device */
	if (priv->pdev->revision < 0x1) {
@@ -534,6 +556,12 @@ static int gve_adminq_issue_cmd(struct gve_priv *priv,
	case GVE_ADMINQ_CONFIGURE_FLOW_RULE:
		priv->adminq_cfg_flow_rule_cnt++;
		break;
	case GVE_ADMINQ_CONFIGURE_RSS:
		priv->adminq_cfg_rss_cnt++;
		break;
	case GVE_ADMINQ_QUERY_RSS:
		priv->adminq_query_rss_cnt++;
		break;
	default:
		dev_err(&priv->pdev->dev, "unknown AQ command opcode %d\n", opcode);
	}
@@ -867,6 +895,8 @@ static void gve_enable_supported_features(struct gve_priv *priv,
					  *dev_op_buffer_sizes,
					  const struct gve_device_option_flow_steering
					  *dev_op_flow_steering,
					  const struct gve_device_option_rss_config
					  *dev_op_rss_config,
					  const struct gve_device_option_modify_ring
					  *dev_op_modify_ring)
{
@@ -931,6 +961,14 @@ static void gve_enable_supported_features(struct gve_priv *priv,
				 priv->max_flow_rules);
		}
	}

	if (dev_op_rss_config &&
	    (supported_features_mask & GVE_SUP_RSS_CONFIG_MASK)) {
		priv->rss_key_size =
			be16_to_cpu(dev_op_rss_config->hash_key_size);
		priv->rss_lut_size =
			be16_to_cpu(dev_op_rss_config->hash_lut_size);
	}
}

int gve_adminq_describe_device(struct gve_priv *priv)
@@ -939,6 +977,7 @@ int gve_adminq_describe_device(struct gve_priv *priv)
	struct gve_device_option_buffer_sizes *dev_op_buffer_sizes = NULL;
	struct gve_device_option_jumbo_frames *dev_op_jumbo_frames = NULL;
	struct gve_device_option_modify_ring *dev_op_modify_ring = NULL;
	struct gve_device_option_rss_config *dev_op_rss_config = NULL;
	struct gve_device_option_gqi_rda *dev_op_gqi_rda = NULL;
	struct gve_device_option_gqi_qpl *dev_op_gqi_qpl = NULL;
	struct gve_device_option_dqo_rda *dev_op_dqo_rda = NULL;
@@ -973,6 +1012,7 @@ int gve_adminq_describe_device(struct gve_priv *priv)
					 &dev_op_jumbo_frames, &dev_op_dqo_qpl,
					 &dev_op_buffer_sizes,
					 &dev_op_flow_steering,
					 &dev_op_rss_config,
					 &dev_op_modify_ring);
	if (err)
		goto free_device_descriptor;
@@ -1035,7 +1075,7 @@ int gve_adminq_describe_device(struct gve_priv *priv)
	gve_enable_supported_features(priv, supported_features_mask,
				      dev_op_jumbo_frames, dev_op_dqo_qpl,
				      dev_op_buffer_sizes, dev_op_flow_steering,
				      dev_op_modify_ring);
				      dev_op_rss_config, dev_op_modify_ring);

free_device_descriptor:
	dma_pool_free(priv->adminq_pool, descriptor, descriptor_bus);
@@ -1248,6 +1288,81 @@ int gve_adminq_reset_flow_rules(struct gve_priv *priv)
	return gve_adminq_configure_flow_rule(priv, &flow_rule_cmd);
}

int gve_adminq_configure_rss(struct gve_priv *priv, struct ethtool_rxfh_param *rxfh)
{
	dma_addr_t lut_bus = 0, key_bus = 0;
	u16 key_size = 0, lut_size = 0;
	union gve_adminq_command cmd;
	__be32 *lut = NULL;
	u8 hash_alg = 0;
	u8 *key = NULL;
	int err = 0;
	u16 i;

	switch (rxfh->hfunc) {
	case ETH_RSS_HASH_NO_CHANGE:
		break;
	case ETH_RSS_HASH_TOP:
		hash_alg = ETH_RSS_HASH_TOP;
		break;
	default:
		return -EOPNOTSUPP;
	}

	if (rxfh->indir) {
		lut_size = priv->rss_lut_size;
		lut = dma_alloc_coherent(&priv->pdev->dev,
					 lut_size * sizeof(*lut),
					 &lut_bus, GFP_KERNEL);
		if (!lut)
			return -ENOMEM;

		for (i = 0; i < priv->rss_lut_size; i++)
			lut[i] = cpu_to_be32(rxfh->indir[i]);
	}

	if (rxfh->key) {
		key_size = priv->rss_key_size;
		key = dma_alloc_coherent(&priv->pdev->dev,
					 key_size, &key_bus, GFP_KERNEL);
		if (!key) {
			err = -ENOMEM;
			goto out;
		}

		memcpy(key, rxfh->key, key_size);
	}

	/* Zero-valued fields in the cmd.configure_rss instruct the device to
	 * not update those fields.
	 */
	memset(&cmd, 0, sizeof(cmd));
	cmd.opcode = cpu_to_be32(GVE_ADMINQ_CONFIGURE_RSS);
	cmd.configure_rss = (struct gve_adminq_configure_rss) {
		.hash_types = cpu_to_be16(BIT(GVE_RSS_HASH_TCPV4) |
					  BIT(GVE_RSS_HASH_UDPV4) |
					  BIT(GVE_RSS_HASH_TCPV6) |
					  BIT(GVE_RSS_HASH_UDPV6)),
		.hash_alg = hash_alg,
		.hash_key_size = cpu_to_be16(key_size),
		.hash_lut_size = cpu_to_be16(lut_size),
		.hash_key_addr = cpu_to_be64(key_bus),
		.hash_lut_addr = cpu_to_be64(lut_bus),
	};

	err = gve_adminq_execute_cmd(priv, &cmd);

out:
	if (lut)
		dma_free_coherent(&priv->pdev->dev,
				  lut_size * sizeof(*lut),
				  lut, lut_bus);
	if (key)
		dma_free_coherent(&priv->pdev->dev,
				  key_size, key, key_bus);
	return err;
}

/* In the dma memory that the driver allocated for the device to query the flow rules, the device
 * will first write it with a struct of gve_query_flow_rules_descriptor. Next to it, the device
 * will write an array of rules or rule ids with the count that specified in the descriptor.
@@ -1325,3 +1440,66 @@ int gve_adminq_query_flow_rules(struct gve_priv *priv, u16 query_opcode, u32 sta
	dma_pool_free(priv->adminq_pool, descriptor, descriptor_bus);
	return err;
}

static int gve_adminq_process_rss_query(struct gve_priv *priv,
					struct gve_query_rss_descriptor *descriptor,
					struct ethtool_rxfh_param *rxfh)
{
	u32 total_memory_length;
	u16 hash_lut_length;
	void *rss_info_addr;
	__be32 *lut;
	u16 i;

	total_memory_length = be32_to_cpu(descriptor->total_length);
	hash_lut_length = priv->rss_lut_size * sizeof(*rxfh->indir);

	if (sizeof(*descriptor) + priv->rss_key_size + hash_lut_length != total_memory_length) {
		dev_err(&priv->dev->dev,
			"rss query desc from device has invalid length parameter.\n");
		return -EINVAL;
	}

	rxfh->hfunc = descriptor->hash_alg;

	rss_info_addr = (void *)(descriptor + 1);
	if (rxfh->key)
		memcpy(rxfh->key, rss_info_addr, priv->rss_key_size);

	rss_info_addr += priv->rss_key_size;
	lut = (__be32 *)rss_info_addr;
	if (rxfh->indir) {
		for (i = 0; i < priv->rss_lut_size; i++)
			rxfh->indir[i] = be32_to_cpu(lut[i]);
	}

	return 0;
}

int gve_adminq_query_rss_config(struct gve_priv *priv, struct ethtool_rxfh_param *rxfh)
{
	struct gve_query_rss_descriptor *descriptor;
	union gve_adminq_command cmd;
	dma_addr_t descriptor_bus;
	int err = 0;

	descriptor = dma_pool_alloc(priv->adminq_pool, GFP_KERNEL, &descriptor_bus);
	if (!descriptor)
		return -ENOMEM;

	memset(&cmd, 0, sizeof(cmd));
	cmd.opcode = cpu_to_be32(GVE_ADMINQ_QUERY_RSS);
	cmd.query_rss = (struct gve_adminq_query_rss) {
		.available_length = cpu_to_be64(GVE_ADMINQ_BUFFER_SIZE),
		.rss_descriptor_addr = cpu_to_be64(descriptor_bus),
	};
	err = gve_adminq_execute_cmd(priv, &cmd);
	if (err)
		goto out;

	err = gve_adminq_process_rss_query(priv, descriptor, rxfh);

out:
	dma_pool_free(priv->adminq_pool, descriptor, descriptor_bus);
	return err;
}
+58 −1
Original line number Diff line number Diff line
@@ -20,12 +20,14 @@ enum gve_adminq_opcodes {
	GVE_ADMINQ_DESTROY_TX_QUEUE		= 0x7,
	GVE_ADMINQ_DESTROY_RX_QUEUE		= 0x8,
	GVE_ADMINQ_DECONFIGURE_DEVICE_RESOURCES	= 0x9,
	GVE_ADMINQ_CONFIGURE_RSS		= 0xA,
	GVE_ADMINQ_SET_DRIVER_PARAMETER		= 0xB,
	GVE_ADMINQ_REPORT_STATS			= 0xC,
	GVE_ADMINQ_REPORT_LINK_SPEED		= 0xD,
	GVE_ADMINQ_GET_PTYPE_MAP		= 0xE,
	GVE_ADMINQ_VERIFY_DRIVER_COMPATIBILITY	= 0xF,
	GVE_ADMINQ_QUERY_FLOW_RULES		= 0x10,
	GVE_ADMINQ_QUERY_RSS			= 0x12,

	/* For commands that are larger than 56 bytes */
	GVE_ADMINQ_EXTENDED_COMMAND		= 0xFF,
@@ -164,6 +166,14 @@ struct gve_device_option_flow_steering {

static_assert(sizeof(struct gve_device_option_flow_steering) == 12);

struct gve_device_option_rss_config {
	__be32 supported_features_mask;
	__be16 hash_key_size;
	__be16 hash_lut_size;
};

static_assert(sizeof(struct gve_device_option_rss_config) == 8);

/* Terminology:
 *
 * RDA - Raw DMA Addressing - Buffers associated with SKBs are directly DMA
@@ -182,6 +192,7 @@ enum gve_dev_opt_id {
	GVE_DEV_OPT_ID_JUMBO_FRAMES		= 0x8,
	GVE_DEV_OPT_ID_BUFFER_SIZES		= 0xa,
	GVE_DEV_OPT_ID_FLOW_STEERING		= 0xb,
	GVE_DEV_OPT_ID_RSS_CONFIG		= 0xe,
};

enum gve_dev_opt_req_feat_mask {
@@ -194,6 +205,7 @@ enum gve_dev_opt_req_feat_mask {
	GVE_DEV_OPT_REQ_FEAT_MASK_BUFFER_SIZES		= 0x0,
	GVE_DEV_OPT_REQ_FEAT_MASK_MODIFY_RING		= 0x0,
	GVE_DEV_OPT_REQ_FEAT_MASK_FLOW_STEERING		= 0x0,
	GVE_DEV_OPT_REQ_FEAT_MASK_RSS_CONFIG		= 0x0,
};

enum gve_sup_feature_mask {
@@ -201,6 +213,7 @@ enum gve_sup_feature_mask {
	GVE_SUP_JUMBO_FRAMES_MASK	= 1 << 2,
	GVE_SUP_BUFFER_SIZES_MASK	= 1 << 4,
	GVE_SUP_FLOW_STEERING_MASK	= 1 << 5,
	GVE_SUP_RSS_CONFIG_MASK		= 1 << 7,
};

#define GVE_DEV_OPT_LEN_GQI_RAW_ADDRESSING 0x0
@@ -214,6 +227,7 @@ enum gve_driver_capbility {
	gve_driver_capability_dqo_rda = 3,
	gve_driver_capability_alt_miss_compl = 4,
	gve_driver_capability_flexible_buffer_size = 5,
	gve_driver_capability_flexible_rss_size = 6,
};

#define GVE_CAP1(a) BIT((int)a)
@@ -226,7 +240,8 @@ enum gve_driver_capbility {
	 GVE_CAP1(gve_driver_capability_gqi_rda) | \
	 GVE_CAP1(gve_driver_capability_dqo_rda) | \
	 GVE_CAP1(gve_driver_capability_alt_miss_compl) | \
	 GVE_CAP1(gve_driver_capability_flexible_buffer_size))
	 GVE_CAP1(gve_driver_capability_flexible_buffer_size) | \
	 GVE_CAP1(gve_driver_capability_flexible_rss_size))

#define GVE_DRIVER_CAPABILITY_FLAGS2 0x0
#define GVE_DRIVER_CAPABILITY_FLAGS3 0x0
@@ -509,6 +524,44 @@ struct gve_adminq_query_flow_rules {

static_assert(sizeof(struct gve_adminq_query_flow_rules) == 24);

enum gve_rss_hash_type {
	GVE_RSS_HASH_IPV4,
	GVE_RSS_HASH_TCPV4,
	GVE_RSS_HASH_IPV6,
	GVE_RSS_HASH_IPV6_EX,
	GVE_RSS_HASH_TCPV6,
	GVE_RSS_HASH_TCPV6_EX,
	GVE_RSS_HASH_UDPV4,
	GVE_RSS_HASH_UDPV6,
	GVE_RSS_HASH_UDPV6_EX,
};

struct gve_adminq_configure_rss {
	__be16 hash_types;
	u8 hash_alg;
	u8 reserved;
	__be16 hash_key_size;
	__be16 hash_lut_size;
	__be64 hash_key_addr;
	__be64 hash_lut_addr;
};

static_assert(sizeof(struct gve_adminq_configure_rss) == 24);

struct gve_query_rss_descriptor {
	__be32 total_length;
	__be16 hash_types;
	u8 hash_alg;
	u8 reserved;
};

struct gve_adminq_query_rss {
	__be64 available_length;
	__be64 rss_descriptor_addr;
};

static_assert(sizeof(struct gve_adminq_query_rss) == 16);

union gve_adminq_command {
	struct {
		__be32 opcode;
@@ -530,6 +583,8 @@ union gve_adminq_command {
			struct gve_adminq_verify_driver_compatibility
						verify_driver_compatibility;
			struct gve_adminq_query_flow_rules query_flow_rules;
			struct gve_adminq_configure_rss configure_rss;
			struct gve_adminq_query_rss query_rss;
			struct gve_adminq_extended_command extended_command;
		};
	};
@@ -568,6 +623,8 @@ int gve_adminq_add_flow_rule(struct gve_priv *priv, struct gve_adminq_flow_rule
int gve_adminq_del_flow_rule(struct gve_priv *priv, u32 loc);
int gve_adminq_reset_flow_rules(struct gve_priv *priv);
int gve_adminq_query_flow_rules(struct gve_priv *priv, u16 query_opcode, u32 starting_loc);
int gve_adminq_configure_rss(struct gve_priv *priv, struct ethtool_rxfh_param *rxfh);
int gve_adminq_query_rss_config(struct gve_priv *priv, struct ethtool_rxfh_param *rxfh);

struct gve_ptype_lut;
int gve_adminq_get_ptype_map_dqo(struct gve_priv *priv,
+43 −1
Original line number Diff line number Diff line
@@ -75,7 +75,8 @@ static const char gve_gstrings_adminq_stats[][ETH_GSTRING_LEN] = {
	"adminq_destroy_tx_queue_cnt", "adminq_destroy_rx_queue_cnt",
	"adminq_dcfg_device_resources_cnt", "adminq_set_driver_parameter_cnt",
	"adminq_report_stats_cnt", "adminq_report_link_speed_cnt", "adminq_get_ptype_map_cnt",
	"adminq_query_flow_rules", "adminq_cfg_flow_rule",
	"adminq_query_flow_rules", "adminq_cfg_flow_rule", "adminq_cfg_rss_cnt",
	"adminq_query_rss_cnt",
};

static const char gve_gstrings_priv_flags[][ETH_GSTRING_LEN] = {
@@ -453,6 +454,8 @@ gve_get_ethtool_stats(struct net_device *netdev,
	data[i++] = priv->adminq_get_ptype_map_cnt;
	data[i++] = priv->adminq_query_flow_rules_cnt;
	data[i++] = priv->adminq_cfg_flow_rule_cnt;
	data[i++] = priv->adminq_cfg_rss_cnt;
	data[i++] = priv->adminq_query_rss_cnt;
}

static void gve_get_channels(struct net_device *netdev,
@@ -838,6 +841,41 @@ static int gve_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd, u
	return err;
}

static u32 gve_get_rxfh_key_size(struct net_device *netdev)
{
	struct gve_priv *priv = netdev_priv(netdev);

	return priv->rss_key_size;
}

static u32 gve_get_rxfh_indir_size(struct net_device *netdev)
{
	struct gve_priv *priv = netdev_priv(netdev);

	return priv->rss_lut_size;
}

static int gve_get_rxfh(struct net_device *netdev, struct ethtool_rxfh_param *rxfh)
{
	struct gve_priv *priv = netdev_priv(netdev);

	if (!priv->rss_key_size || !priv->rss_lut_size)
		return -EOPNOTSUPP;

	return gve_adminq_query_rss_config(priv, rxfh);
}

static int gve_set_rxfh(struct net_device *netdev, struct ethtool_rxfh_param *rxfh,
			struct netlink_ext_ack *extack)
{
	struct gve_priv *priv = netdev_priv(netdev);

	if (!priv->rss_key_size || !priv->rss_lut_size)
		return -EOPNOTSUPP;

	return gve_adminq_configure_rss(priv, rxfh);
}

const struct ethtool_ops gve_ethtool_ops = {
	.supported_coalesce_params = ETHTOOL_COALESCE_USECS,
	.supported_ring_params = ETHTOOL_RING_USE_TCP_DATA_SPLIT,
@@ -851,6 +889,10 @@ const struct ethtool_ops gve_ethtool_ops = {
	.get_channels = gve_get_channels,
	.set_rxnfc = gve_set_rxnfc,
	.get_rxnfc = gve_get_rxnfc,
	.get_rxfh_indir_size = gve_get_rxfh_indir_size,
	.get_rxfh_key_size = gve_get_rxfh_key_size,
	.get_rxfh = gve_get_rxfh,
	.set_rxfh = gve_set_rxfh,
	.get_link = ethtool_op_get_link,
	.get_coalesce = gve_get_coalesce,
	.set_coalesce = gve_set_coalesce,