mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-04-13 10:50:40 -04:00
gve: Add RSS adminq commands and ethtool support
Introduce adminq commands to configure and retrieve RSS settings from the device. Implement corresponding ethtool ops for user-level management. Signed-off-by: Jeroen de Borst <jeroendb@google.com> Co-developed-by: Ziwei Xiao <ziweixiao@google.com> Signed-off-by: Ziwei Xiao <ziweixiao@google.com> Signed-off-by: Praveen Kaligineedi <pkaligineedi@google.com> Reviewed-by: Praveen Kaligineedi <pkaligineedi@google.com> Reviewed-by: Harshitha Ramamurthy <hramamurthy@google.com> Reviewed-by: Willem de Bruijn <willemb@google.com> Reviewed-by: Hariprasad Kelam <hkelam@marvell.com> Link: https://patch.msgid.link/20240812222013.1503584-3-pkaligineedi@google.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
committed by
Jakub Kicinski
parent
58c98d0cd4
commit
fa46c456fa
@@ -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 */
|
||||
|
||||
@@ -309,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) {
|
||||
@@ -554,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);
|
||||
}
|
||||
@@ -1280,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.
|
||||
@@ -1357,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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
@@ -522,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;
|
||||
@@ -543,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;
|
||||
};
|
||||
};
|
||||
@@ -581,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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user