Skip to content

Commit

Permalink
hinic: add support for rss parameters with ethtool
Browse files Browse the repository at this point in the history
This patch adds support rss parameters with ethtool,
user can change hash key, hash indirection table, hash
function by ethtool -X, and show rss parameters by ethtool -x.

Signed-off-by: Xue Chaojing <xuechaojing@huawei.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Xue Chaojing authored and David S. Miller committed Jun 19, 2019
1 parent eb8ce9a commit 4fdc51b
Show file tree
Hide file tree
Showing 5 changed files with 479 additions and 1 deletion.
2 changes: 2 additions & 0 deletions drivers/net/ethernet/huawei/hinic/hinic_dev.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ struct hinic_dev {
u16 num_rss;
u16 rss_limit;
struct hinic_rss_type rss_type;
u8 *rss_hkey_user;
s32 *rss_indir_user;
};

#endif
295 changes: 295 additions & 0 deletions drivers/net/ethernet/huawei/hinic/hinic_ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,307 @@ static void hinic_get_channels(struct net_device *netdev,
channels->combined_count = 0;
}

static int hinic_get_rss_hash_opts(struct hinic_dev *nic_dev,
struct ethtool_rxnfc *cmd)
{
struct hinic_rss_type rss_type = { 0 };
int err;

cmd->data = 0;

if (!(nic_dev->flags & HINIC_RSS_ENABLE))
return 0;

err = hinic_get_rss_type(nic_dev, nic_dev->rss_tmpl_idx,
&rss_type);
if (err)
return err;

cmd->data = RXH_IP_SRC | RXH_IP_DST;
switch (cmd->flow_type) {
case TCP_V4_FLOW:
if (rss_type.tcp_ipv4)
cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
break;
case TCP_V6_FLOW:
if (rss_type.tcp_ipv6)
cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
break;
case UDP_V4_FLOW:
if (rss_type.udp_ipv4)
cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
break;
case UDP_V6_FLOW:
if (rss_type.udp_ipv6)
cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
break;
case IPV4_FLOW:
case IPV6_FLOW:
break;
default:
cmd->data = 0;
return -EINVAL;
}

return 0;
}

static int set_l4_rss_hash_ops(struct ethtool_rxnfc *cmd,
struct hinic_rss_type *rss_type)
{
u8 rss_l4_en = 0;

switch (cmd->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
case 0:
rss_l4_en = 0;
break;
case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
rss_l4_en = 1;
break;
default:
return -EINVAL;
}

switch (cmd->flow_type) {
case TCP_V4_FLOW:
rss_type->tcp_ipv4 = rss_l4_en;
break;
case TCP_V6_FLOW:
rss_type->tcp_ipv6 = rss_l4_en;
break;
case UDP_V4_FLOW:
rss_type->udp_ipv4 = rss_l4_en;
break;
case UDP_V6_FLOW:
rss_type->udp_ipv6 = rss_l4_en;
break;
default:
return -EINVAL;
}

return 0;
}

static int hinic_set_rss_hash_opts(struct hinic_dev *nic_dev,
struct ethtool_rxnfc *cmd)
{
struct hinic_rss_type *rss_type = &nic_dev->rss_type;
int err;

if (!(nic_dev->flags & HINIC_RSS_ENABLE)) {
cmd->data = 0;
return -EOPNOTSUPP;
}

/* RSS does not support anything other than hashing
* to queues on src and dst IPs and ports
*/
if (cmd->data & ~(RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 |
RXH_L4_B_2_3))
return -EINVAL;

/* We need at least the IP SRC and DEST fields for hashing */
if (!(cmd->data & RXH_IP_SRC) || !(cmd->data & RXH_IP_DST))
return -EINVAL;

err = hinic_get_rss_type(nic_dev,
nic_dev->rss_tmpl_idx, rss_type);
if (err)
return -EFAULT;

switch (cmd->flow_type) {
case TCP_V4_FLOW:
case TCP_V6_FLOW:
case UDP_V4_FLOW:
case UDP_V6_FLOW:
err = set_l4_rss_hash_ops(cmd, rss_type);
if (err)
return err;
break;
case IPV4_FLOW:
rss_type->ipv4 = 1;
break;
case IPV6_FLOW:
rss_type->ipv6 = 1;
break;
default:
return -EINVAL;
}

err = hinic_set_rss_type(nic_dev, nic_dev->rss_tmpl_idx,
*rss_type);
if (err)
return -EFAULT;

return 0;
}

static int __set_rss_rxfh(struct net_device *netdev,
const u32 *indir, const u8 *key)
{
struct hinic_dev *nic_dev = netdev_priv(netdev);
int err;

if (indir) {
if (!nic_dev->rss_indir_user) {
nic_dev->rss_indir_user =
kzalloc(sizeof(u32) * HINIC_RSS_INDIR_SIZE,
GFP_KERNEL);
if (!nic_dev->rss_indir_user)
return -ENOMEM;
}

memcpy(nic_dev->rss_indir_user, indir,
sizeof(u32) * HINIC_RSS_INDIR_SIZE);

err = hinic_rss_set_indir_tbl(nic_dev,
nic_dev->rss_tmpl_idx, indir);
if (err)
return -EFAULT;
}

if (key) {
if (!nic_dev->rss_hkey_user) {
nic_dev->rss_hkey_user =
kzalloc(HINIC_RSS_KEY_SIZE * 2, GFP_KERNEL);

if (!nic_dev->rss_hkey_user)
return -ENOMEM;
}

memcpy(nic_dev->rss_hkey_user, key, HINIC_RSS_KEY_SIZE);

err = hinic_rss_set_template_tbl(nic_dev,
nic_dev->rss_tmpl_idx, key);
if (err)
return -EFAULT;
}

return 0;
}

static int hinic_get_rxnfc(struct net_device *netdev,
struct ethtool_rxnfc *cmd, u32 *rule_locs)
{
struct hinic_dev *nic_dev = netdev_priv(netdev);
int err = 0;

switch (cmd->cmd) {
case ETHTOOL_GRXRINGS:
cmd->data = nic_dev->num_qps;
break;
case ETHTOOL_GRXFH:
err = hinic_get_rss_hash_opts(nic_dev, cmd);
break;
default:
err = -EOPNOTSUPP;
break;
}

return err;
}

static int hinic_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd)
{
struct hinic_dev *nic_dev = netdev_priv(netdev);
int err = 0;

switch (cmd->cmd) {
case ETHTOOL_SRXFH:
err = hinic_set_rss_hash_opts(nic_dev, cmd);
break;
default:
err = -EOPNOTSUPP;
break;
}

return err;
}

static int hinic_get_rxfh(struct net_device *netdev,
u32 *indir, u8 *key, u8 *hfunc)
{
struct hinic_dev *nic_dev = netdev_priv(netdev);
u8 hash_engine_type = 0;
int err = 0;

if (!(nic_dev->flags & HINIC_RSS_ENABLE))
return -EOPNOTSUPP;

if (hfunc) {
err = hinic_rss_get_hash_engine(nic_dev,
nic_dev->rss_tmpl_idx,
&hash_engine_type);
if (err)
return -EFAULT;

*hfunc = hash_engine_type ? ETH_RSS_HASH_TOP : ETH_RSS_HASH_XOR;
}

if (indir) {
err = hinic_rss_get_indir_tbl(nic_dev,
nic_dev->rss_tmpl_idx, indir);
if (err)
return -EFAULT;
}

if (key)
err = hinic_rss_get_template_tbl(nic_dev,
nic_dev->rss_tmpl_idx, key);

return err;
}

static int hinic_set_rxfh(struct net_device *netdev, const u32 *indir,
const u8 *key, const u8 hfunc)
{
struct hinic_dev *nic_dev = netdev_priv(netdev);
int err = 0;

if (!(nic_dev->flags & HINIC_RSS_ENABLE))
return -EOPNOTSUPP;

if (hfunc != ETH_RSS_HASH_NO_CHANGE) {
if (hfunc != ETH_RSS_HASH_TOP && hfunc != ETH_RSS_HASH_XOR)
return -EOPNOTSUPP;

nic_dev->rss_hash_engine = (hfunc == ETH_RSS_HASH_XOR) ?
HINIC_RSS_HASH_ENGINE_TYPE_XOR :
HINIC_RSS_HASH_ENGINE_TYPE_TOEP;
err = hinic_rss_set_hash_engine
(nic_dev, nic_dev->rss_tmpl_idx,
nic_dev->rss_hash_engine);
if (err)
return -EFAULT;
}

err = __set_rss_rxfh(netdev, indir, key);

return err;
}

static u32 hinic_get_rxfh_key_size(struct net_device *netdev)
{
return HINIC_RSS_KEY_SIZE;
}

static u32 hinic_get_rxfh_indir_size(struct net_device *netdev)
{
return HINIC_RSS_INDIR_SIZE;
}

static const struct ethtool_ops hinic_ethtool_ops = {
.get_link_ksettings = hinic_get_link_ksettings,
.get_drvinfo = hinic_get_drvinfo,
.get_link = ethtool_op_get_link,
.get_ringparam = hinic_get_ringparam,
.get_channels = hinic_get_channels,
.get_rxnfc = hinic_get_rxnfc,
.set_rxnfc = hinic_set_rxnfc,
.get_rxfh_key_size = hinic_get_rxfh_key_size,
.get_rxfh_indir_size = hinic_get_rxfh_indir_size,
.get_rxfh = hinic_get_rxfh,
.set_rxfh = hinic_set_rxfh,
};

void hinic_set_ethtool_ops(struct net_device *netdev)
Expand Down
12 changes: 11 additions & 1 deletion drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,21 @@ enum hinic_port_cmd {

HINIC_PORT_CMD_SET_RX_CSUM = 26,

HINIC_PORT_CMD_GET_RSS_TEMPLATE_INDIR_TBL = 37,

HINIC_PORT_CMD_SET_PORT_STATE = 41,

HINIC_PORT_CMD_SET_RSS_TEMPLATE_TBL = 43,

HINIC_PORT_CMD_SET_RSS_HASH_ENGINE = 45,
HINIC_PORT_CMD_GET_RSS_TEMPLATE_TBL = 44,

HINIC_PORT_CMD_SET_RSS_HASH_ENGINE = 45,

HINIC_PORT_CMD_GET_RSS_HASH_ENGINE = 46,

HINIC_PORT_CMD_GET_RSS_CTX_TBL = 47,

HINIC_PORT_CMD_SET_RSS_CTX_TBL = 48,

HINIC_PORT_CMD_RSS_TEMP_MGR = 49,

Expand Down
Loading

0 comments on commit 4fdc51b

Please sign in to comment.