Merge branch 'net-ethtool-add-dedicated-rxfh-driver-callbacks'

Jakub Kicinski says:

====================
net: ethtool: add dedicated RXFH driver callbacks

Andrew asked me to plumb the RXFH header fields configuration
thru to netlink. Before we do that we need to clean up the driver
facing API a little bit. Right now RXFH configuration shares the
callbacks with n-tuple filters. The future of n-tuple filters
is uncertain within netlink. Separate the two for clarity both
of the core code and the driver facing API.

This series adds the new callbacks and converts the initial
handful of drivers. There is 31 more driver patches to come,
then we can stop calling rxnfc in the core for rxfh.
====================

Link: https://patch.msgid.link/20250611145949.2674086-1-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2025-06-12 17:16:23 -07:00
commit 8f9fee2595
10 changed files with 239 additions and 177 deletions

View File

@ -798,9 +798,6 @@ static int gve_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd)
case ETHTOOL_SRXCLSRLDEL:
err = gve_del_flow_rule(priv, cmd);
break;
case ETHTOOL_SRXFH:
err = -EOPNOTSUPP;
break;
default:
err = -EOPNOTSUPP;
break;
@ -835,9 +832,6 @@ static int gve_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd, u
case ETHTOOL_GRXCLSRLALL:
err = gve_get_flow_rule_ids(priv, cmd, (u32 *)rule_locs);
break;
case ETHTOOL_GRXFH:
err = -EOPNOTSUPP;
break;
default:
err = -EOPNOTSUPP;
break;

View File

@ -5014,8 +5014,6 @@ static int mvneta_ethtool_get_rxnfc(struct net_device *dev,
case ETHTOOL_GRXRINGS:
info->data = rxq_number;
return 0;
case ETHTOOL_GRXFH:
return -EOPNOTSUPP;
default:
return -EOPNOTSUPP;
}

View File

@ -2616,6 +2616,7 @@ static void mlx5e_get_ts_stats(struct net_device *netdev,
const struct ethtool_ops mlx5e_ethtool_ops = {
.cap_link_lanes_supported = true,
.cap_rss_ctx_supported = true,
.rxfh_per_ctx_fields = true,
.rxfh_per_ctx_key = true,
.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
ETHTOOL_COALESCE_MAX_FRAMES |

View File

@ -531,20 +531,6 @@ static int fbnic_get_rss_hash_idx(u32 flow_type)
return -1;
}
static int
fbnic_get_rss_hash_opts(struct fbnic_net *fbn, struct ethtool_rxnfc *cmd)
{
int hash_opt_idx = fbnic_get_rss_hash_idx(cmd->flow_type);
if (hash_opt_idx < 0)
return -EINVAL;
/* Report options from rss_en table in fbn */
cmd->data = fbn->rss_flow_hash[hash_opt_idx];
return 0;
}
static int fbnic_get_cls_rule_all(struct fbnic_net *fbn,
struct ethtool_rxnfc *cmd,
u32 *rule_locs)
@ -779,9 +765,6 @@ static int fbnic_get_rxnfc(struct net_device *netdev,
cmd->data = fbn->num_rx_queues;
ret = 0;
break;
case ETHTOOL_GRXFH:
ret = fbnic_get_rss_hash_opts(fbn, cmd);
break;
case ETHTOOL_GRXCLSRULE:
ret = fbnic_get_cls_rule(fbn, cmd);
break;
@ -803,41 +786,6 @@ static int fbnic_get_rxnfc(struct net_device *netdev,
return ret;
}
#define FBNIC_L2_HASH_OPTIONS \
(RXH_L2DA | RXH_DISCARD)
#define FBNIC_L3_HASH_OPTIONS \
(FBNIC_L2_HASH_OPTIONS | RXH_IP_SRC | RXH_IP_DST)
#define FBNIC_L4_HASH_OPTIONS \
(FBNIC_L3_HASH_OPTIONS | RXH_L4_B_0_1 | RXH_L4_B_2_3)
static int
fbnic_set_rss_hash_opts(struct fbnic_net *fbn, const struct ethtool_rxnfc *cmd)
{
int hash_opt_idx;
/* Verify the type requested is correct */
hash_opt_idx = fbnic_get_rss_hash_idx(cmd->flow_type);
if (hash_opt_idx < 0)
return -EINVAL;
/* Verify the fields asked for can actually be assigned based on type */
if (cmd->data & ~FBNIC_L4_HASH_OPTIONS ||
(hash_opt_idx > FBNIC_L4_HASH_OPT &&
cmd->data & ~FBNIC_L3_HASH_OPTIONS) ||
(hash_opt_idx > FBNIC_IP_HASH_OPT &&
cmd->data & ~FBNIC_L2_HASH_OPTIONS))
return -EINVAL;
fbn->rss_flow_hash[hash_opt_idx] = cmd->data;
if (netif_running(fbn->netdev)) {
fbnic_rss_reinit(fbn->fbd, fbn);
fbnic_write_rules(fbn->fbd);
}
return 0;
}
static int fbnic_cls_rule_any_loc(struct fbnic_dev *fbd)
{
int i;
@ -1244,9 +1192,6 @@ static int fbnic_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd)
int ret = -EOPNOTSUPP;
switch (cmd->cmd) {
case ETHTOOL_SRXFH:
ret = fbnic_set_rss_hash_opts(fbn, cmd);
break;
case ETHTOOL_SRXCLSRLINS:
ret = fbnic_set_cls_rule_ins(fbn, cmd);
break;
@ -1346,6 +1291,60 @@ fbnic_set_rxfh(struct net_device *netdev, struct ethtool_rxfh_param *rxfh,
return 0;
}
static int
fbnic_get_rss_hash_opts(struct net_device *netdev,
struct ethtool_rxfh_fields *cmd)
{
int hash_opt_idx = fbnic_get_rss_hash_idx(cmd->flow_type);
struct fbnic_net *fbn = netdev_priv(netdev);
if (hash_opt_idx < 0)
return -EINVAL;
/* Report options from rss_en table in fbn */
cmd->data = fbn->rss_flow_hash[hash_opt_idx];
return 0;
}
#define FBNIC_L2_HASH_OPTIONS \
(RXH_L2DA | RXH_DISCARD)
#define FBNIC_L3_HASH_OPTIONS \
(FBNIC_L2_HASH_OPTIONS | RXH_IP_SRC | RXH_IP_DST)
#define FBNIC_L4_HASH_OPTIONS \
(FBNIC_L3_HASH_OPTIONS | RXH_L4_B_0_1 | RXH_L4_B_2_3)
static int
fbnic_set_rss_hash_opts(struct net_device *netdev,
const struct ethtool_rxfh_fields *cmd,
struct netlink_ext_ack *extack)
{
struct fbnic_net *fbn = netdev_priv(netdev);
int hash_opt_idx;
/* Verify the type requested is correct */
hash_opt_idx = fbnic_get_rss_hash_idx(cmd->flow_type);
if (hash_opt_idx < 0)
return -EINVAL;
/* Verify the fields asked for can actually be assigned based on type */
if (cmd->data & ~FBNIC_L4_HASH_OPTIONS ||
(hash_opt_idx > FBNIC_L4_HASH_OPT &&
cmd->data & ~FBNIC_L3_HASH_OPTIONS) ||
(hash_opt_idx > FBNIC_IP_HASH_OPT &&
cmd->data & ~FBNIC_L2_HASH_OPTIONS))
return -EINVAL;
fbn->rss_flow_hash[hash_opt_idx] = cmd->data;
if (netif_running(fbn->netdev)) {
fbnic_rss_reinit(fbn->fbd, fbn);
fbnic_write_rules(fbn->fbd);
}
return 0;
}
static int
fbnic_modify_rxfh_context(struct net_device *netdev,
struct ethtool_rxfh_context *ctx,
@ -1697,6 +1696,8 @@ static const struct ethtool_ops fbnic_ethtool_ops = {
.get_rxfh_indir_size = fbnic_get_rxfh_indir_size,
.get_rxfh = fbnic_get_rxfh,
.set_rxfh = fbnic_set_rxfh,
.get_rxfh_fields = fbnic_get_rss_hash_opts,
.set_rxfh_fields = fbnic_set_rss_hash_opts,
.create_rxfh_context = fbnic_create_rxfh_context,
.modify_rxfh_context = fbnic_modify_rxfh_context,
.remove_rxfh_context = fbnic_remove_rxfh_context,

View File

@ -262,6 +262,7 @@ const struct ethtool_ops efx_ethtool_ops = {
.set_rxnfc = efx_ethtool_set_rxnfc,
.get_rxfh_indir_size = efx_ethtool_get_rxfh_indir_size,
.get_rxfh_key_size = efx_ethtool_get_rxfh_key_size,
.rxfh_per_ctx_fields = true,
.rxfh_per_ctx_key = true,
.cap_rss_rxnfc_adds = true,
.rxfh_priv_size = sizeof(struct efx_rss_context_priv),

View File

@ -1580,9 +1580,10 @@ static void netvsc_get_strings(struct net_device *dev, u32 stringset, u8 *data)
}
static int
netvsc_get_rss_hash_opts(struct net_device_context *ndc,
struct ethtool_rxnfc *info)
netvsc_get_rxfh_fields(struct net_device *ndev,
struct ethtool_rxfh_fields *info)
{
struct net_device_context *ndc = netdev_priv(ndev);
const u32 l4_flag = RXH_L4_B_0_1 | RXH_L4_B_2_3;
info->data = RXH_IP_SRC | RXH_IP_DST;
@ -1637,16 +1638,17 @@ netvsc_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info,
case ETHTOOL_GRXRINGS:
info->data = nvdev->num_chn;
return 0;
case ETHTOOL_GRXFH:
return netvsc_get_rss_hash_opts(ndc, info);
}
return -EOPNOTSUPP;
}
static int netvsc_set_rss_hash_opts(struct net_device_context *ndc,
struct ethtool_rxnfc *info)
static int
netvsc_set_rxfh_fields(struct net_device *dev,
const struct ethtool_rxfh_fields *info,
struct netlink_ext_ack *extack)
{
struct net_device_context *ndc = netdev_priv(dev);
if (info->data == (RXH_IP_SRC | RXH_IP_DST |
RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
switch (info->flow_type) {
@ -1701,17 +1703,6 @@ static int netvsc_set_rss_hash_opts(struct net_device_context *ndc,
return -EOPNOTSUPP;
}
static int
netvsc_set_rxnfc(struct net_device *ndev, struct ethtool_rxnfc *info)
{
struct net_device_context *ndc = netdev_priv(ndev);
if (info->cmd == ETHTOOL_SRXFH)
return netvsc_set_rss_hash_opts(ndc, info);
return -EOPNOTSUPP;
}
static u32 netvsc_get_rxfh_key_size(struct net_device *dev)
{
return NETVSC_HASH_KEYLEN;
@ -1979,11 +1970,12 @@ static const struct ethtool_ops ethtool_ops = {
.set_channels = netvsc_set_channels,
.get_ts_info = ethtool_op_get_ts_info,
.get_rxnfc = netvsc_get_rxnfc,
.set_rxnfc = netvsc_set_rxnfc,
.get_rxfh_key_size = netvsc_get_rxfh_key_size,
.get_rxfh_indir_size = netvsc_rss_indir_size,
.get_rxfh = netvsc_get_rxfh,
.set_rxfh = netvsc_set_rxfh,
.get_rxfh_fields = netvsc_get_rxfh_fields,
.set_rxfh_fields = netvsc_set_rxfh_fields,
.get_link_ksettings = netvsc_get_link_ksettings,
.set_link_ksettings = netvsc_set_link_ksettings,
.get_ringparam = netvsc_get_ringparam,

View File

@ -4193,8 +4193,11 @@ static void virtnet_init_default_rss(struct virtnet_info *vi)
netdev_rss_key_fill(vi->rss_hash_key_data, vi->rss_key_size);
}
static void virtnet_get_hashflow(const struct virtnet_info *vi, struct ethtool_rxnfc *info)
static int virtnet_get_hashflow(struct net_device *dev,
struct ethtool_rxfh_fields *info)
{
struct virtnet_info *vi = netdev_priv(dev);
info->data = 0;
switch (info->flow_type) {
case TCP_V4_FLOW:
@ -4243,17 +4246,22 @@ static void virtnet_get_hashflow(const struct virtnet_info *vi, struct ethtool_r
info->data = 0;
break;
}
return 0;
}
static bool virtnet_set_hashflow(struct virtnet_info *vi, struct ethtool_rxnfc *info)
static int virtnet_set_hashflow(struct net_device *dev,
const struct ethtool_rxfh_fields *info,
struct netlink_ext_ack *extack)
{
struct virtnet_info *vi = netdev_priv(dev);
u32 new_hashtypes = vi->rss_hash_types_saved;
bool is_disable = info->data & RXH_DISCARD;
bool is_l4 = info->data == (RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3);
/* supports only 'sd', 'sdfn' and 'r' */
if (!((info->data == (RXH_IP_SRC | RXH_IP_DST)) | is_l4 | is_disable))
return false;
return -EINVAL;
switch (info->flow_type) {
case TCP_V4_FLOW:
@ -4292,21 +4300,22 @@ static bool virtnet_set_hashflow(struct virtnet_info *vi, struct ethtool_rxnfc *
break;
default:
/* unsupported flow */
return false;
return -EINVAL;
}
/* if unsupported hashtype was set */
if (new_hashtypes != (new_hashtypes & vi->rss_hash_types_supported))
return false;
return -EINVAL;
if (new_hashtypes != vi->rss_hash_types_saved) {
vi->rss_hash_types_saved = new_hashtypes;
vi->rss_hdr->hash_types = cpu_to_le32(vi->rss_hash_types_saved);
if (vi->dev->features & NETIF_F_RXHASH)
return virtnet_commit_rss_command(vi);
if (!virtnet_commit_rss_command(vi))
return -EINVAL;
}
return true;
return 0;
}
static void virtnet_get_drvinfo(struct net_device *dev,
@ -5539,27 +5548,6 @@ static int virtnet_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info,
switch (info->cmd) {
case ETHTOOL_GRXRINGS:
info->data = vi->curr_queue_pairs;
break;
case ETHTOOL_GRXFH:
virtnet_get_hashflow(vi, info);
break;
default:
rc = -EOPNOTSUPP;
}
return rc;
}
static int virtnet_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info)
{
struct virtnet_info *vi = netdev_priv(dev);
int rc = 0;
switch (info->cmd) {
case ETHTOOL_SRXFH:
if (!virtnet_set_hashflow(vi, info))
rc = -EINVAL;
break;
default:
rc = -EOPNOTSUPP;
@ -5591,8 +5579,9 @@ static const struct ethtool_ops virtnet_ethtool_ops = {
.get_rxfh_indir_size = virtnet_get_rxfh_indir_size,
.get_rxfh = virtnet_get_rxfh,
.set_rxfh = virtnet_set_rxfh,
.get_rxfh_fields = virtnet_get_hashflow,
.set_rxfh_fields = virtnet_set_hashflow,
.get_rxnfc = virtnet_get_rxnfc,
.set_rxnfc = virtnet_set_rxnfc,
};
static void virtnet_get_queue_stats_rx(struct net_device *dev, int i,

View File

@ -833,11 +833,19 @@ vmxnet3_set_ringparam(struct net_device *netdev,
}
static int
vmxnet3_get_rss_hash_opts(struct vmxnet3_adapter *adapter,
struct ethtool_rxnfc *info)
vmxnet3_get_rss_hash_opts(struct net_device *netdev,
struct ethtool_rxfh_fields *info)
{
struct vmxnet3_adapter *adapter = netdev_priv(netdev);
enum Vmxnet3_RSSField rss_fields;
if (!VMXNET3_VERSION_GE_4(adapter))
return -EOPNOTSUPP;
#ifdef VMXNET3_RSS
if (!adapter->rss)
return -EOPNOTSUPP;
#endif
if (netif_running(adapter->netdev)) {
unsigned long flags;
@ -900,10 +908,20 @@ vmxnet3_get_rss_hash_opts(struct vmxnet3_adapter *adapter,
static int
vmxnet3_set_rss_hash_opt(struct net_device *netdev,
struct vmxnet3_adapter *adapter,
struct ethtool_rxnfc *nfc)
const struct ethtool_rxfh_fields *nfc,
struct netlink_ext_ack *extack)
{
enum Vmxnet3_RSSField rss_fields = adapter->rss_fields;
struct vmxnet3_adapter *adapter = netdev_priv(netdev);
enum Vmxnet3_RSSField rss_fields;
if (!VMXNET3_VERSION_GE_4(adapter))
return -EOPNOTSUPP;
#ifdef VMXNET3_RSS
if (!adapter->rss)
return -EOPNOTSUPP;
#endif
rss_fields = adapter->rss_fields;
/* RSS does not support anything other than hashing
* to queues on src and dst IPs and ports
@ -1074,19 +1092,6 @@ vmxnet3_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *info,
case ETHTOOL_GRXRINGS:
info->data = adapter->num_rx_queues;
break;
case ETHTOOL_GRXFH:
if (!VMXNET3_VERSION_GE_4(adapter)) {
err = -EOPNOTSUPP;
break;
}
#ifdef VMXNET3_RSS
if (!adapter->rss) {
err = -EOPNOTSUPP;
break;
}
#endif
err = vmxnet3_get_rss_hash_opts(adapter, info);
break;
default:
err = -EOPNOTSUPP;
break;
@ -1095,36 +1100,6 @@ vmxnet3_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *info,
return err;
}
static int
vmxnet3_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *info)
{
struct vmxnet3_adapter *adapter = netdev_priv(netdev);
int err = 0;
if (!VMXNET3_VERSION_GE_4(adapter)) {
err = -EOPNOTSUPP;
goto done;
}
#ifdef VMXNET3_RSS
if (!adapter->rss) {
err = -EOPNOTSUPP;
goto done;
}
#endif
switch (info->cmd) {
case ETHTOOL_SRXFH:
err = vmxnet3_set_rss_hash_opt(netdev, adapter, info);
break;
default:
err = -EOPNOTSUPP;
break;
}
done:
return err;
}
#ifdef VMXNET3_RSS
static u32
vmxnet3_get_rss_indir_size(struct net_device *netdev)
@ -1361,12 +1336,13 @@ static const struct ethtool_ops vmxnet3_ethtool_ops = {
.get_ringparam = vmxnet3_get_ringparam,
.set_ringparam = vmxnet3_set_ringparam,
.get_rxnfc = vmxnet3_get_rxnfc,
.set_rxnfc = vmxnet3_set_rxnfc,
#ifdef VMXNET3_RSS
.get_rxfh_indir_size = vmxnet3_get_rss_indir_size,
.get_rxfh = vmxnet3_get_rss,
.set_rxfh = vmxnet3_set_rss,
#endif
.get_rxfh_fields = vmxnet3_get_rss_hash_opts,
.set_rxfh_fields = vmxnet3_set_rss_hash_opt,
.get_link_ksettings = vmxnet3_get_link_ksettings,
.get_channels = vmxnet3_get_channels,
};

View File

@ -825,6 +825,19 @@ struct ethtool_rxfh_param {
u8 input_xfrm;
};
/**
* struct ethtool_rxfh_fields - Rx Flow Hashing (RXFH) header field config
* @data: which header fields are used for hashing, bitmask of RXH_* defines
* @flow_type: L2-L4 network traffic flow type
* @rss_context: RSS context, will only be used if rxfh_per_ctx_fields is
* set in struct ethtool_ops
*/
struct ethtool_rxfh_fields {
u32 data;
u32 flow_type;
u32 rss_context;
};
/**
* struct kernel_ethtool_ts_info - kernel copy of struct ethtool_ts_info
* @cmd: command number = %ETHTOOL_GET_TS_INFO
@ -855,6 +868,8 @@ struct kernel_ethtool_ts_info {
* @cap_rss_ctx_supported: indicates if the driver supports RSS
* contexts via legacy API, drivers implementing @create_rxfh_context
* do not have to set this bit.
* @rxfh_per_ctx_fields: device supports selecting different header fields
* for Rx hash calculation and RSS for each additional context.
* @rxfh_per_ctx_key: device supports setting different RSS key for each
* additional context. Netlink API should report hfunc, key, and input_xfrm
* for every context, not just context 0.
@ -968,6 +983,8 @@ struct kernel_ethtool_ts_info {
* will remain unchanged.
* Returns a negative error code or zero. An error code must be returned
* if at least one unsupported change was requested.
* @get_rxfh_fields: Get header fields used for flow hashing.
* @set_rxfh_fields: Set header fields used for flow hashing.
* @create_rxfh_context: Create a new RSS context with the specified RX flow
* hash indirection table, hash key, and hash function.
* The &struct ethtool_rxfh_context for this context is passed in @ctx;
@ -1084,6 +1101,7 @@ struct ethtool_ops {
u32 supported_input_xfrm:8;
u32 cap_link_lanes_supported:1;
u32 cap_rss_ctx_supported:1;
u32 rxfh_per_ctx_fields:1;
u32 rxfh_per_ctx_key:1;
u32 cap_rss_rxnfc_adds:1;
u32 rxfh_indir_space;
@ -1153,6 +1171,11 @@ struct ethtool_ops {
int (*get_rxfh)(struct net_device *, struct ethtool_rxfh_param *);
int (*set_rxfh)(struct net_device *, struct ethtool_rxfh_param *,
struct netlink_ext_ack *extack);
int (*get_rxfh_fields)(struct net_device *,
struct ethtool_rxfh_fields *);
int (*set_rxfh_fields)(struct net_device *,
const struct ethtool_rxfh_fields *,
struct netlink_ext_ack *extack);
int (*create_rxfh_context)(struct net_device *,
struct ethtool_rxfh_context *ctx,
const struct ethtool_rxfh_param *rxfh,

View File

@ -1048,9 +1048,20 @@ static int ethtool_check_flow_types(struct net_device *dev, u32 input_xfrm)
continue;
info.flow_type = i;
err = ops->get_rxnfc(dev, &info, NULL);
if (err)
continue;
if (ops->get_rxfh_fields) {
struct ethtool_rxfh_fields fields = {
.flow_type = info.flow_type,
};
if (ops->get_rxfh_fields(dev, &fields))
continue;
info.data = fields.data;
} else {
if (ops->get_rxnfc(dev, &info, NULL))
continue;
}
err = ethtool_check_xfrm_rxfh(input_xfrm, info.data);
if (err)
@ -1060,6 +1071,90 @@ static int ethtool_check_flow_types(struct net_device *dev, u32 input_xfrm)
return 0;
}
static noinline_for_stack int
ethtool_set_rxfh_fields(struct net_device *dev, u32 cmd, void __user *useraddr)
{
const struct ethtool_ops *ops = dev->ethtool_ops;
struct ethtool_rxfh_fields fields = {};
struct ethtool_rxnfc info;
size_t info_size = sizeof(info);
int rc;
if (!ops->set_rxnfc && !ops->set_rxfh_fields)
return -EOPNOTSUPP;
rc = ethtool_rxnfc_copy_struct(cmd, &info, &info_size, useraddr);
if (rc)
return rc;
if (info.flow_type & FLOW_RSS && info.rss_context &&
!ops->rxfh_per_ctx_fields)
return -EINVAL;
if (ops->get_rxfh) {
struct ethtool_rxfh_param rxfh = {};
rc = ops->get_rxfh(dev, &rxfh);
if (rc)
return rc;
rc = ethtool_check_xfrm_rxfh(rxfh.input_xfrm, info.data);
if (rc)
return rc;
}
if (!ops->set_rxfh_fields)
return ops->set_rxnfc(dev, &info);
fields.data = info.data;
fields.flow_type = info.flow_type & ~FLOW_RSS;
if (info.flow_type & FLOW_RSS)
fields.rss_context = info.rss_context;
return ops->set_rxfh_fields(dev, &fields, NULL);
}
static noinline_for_stack int
ethtool_get_rxfh_fields(struct net_device *dev, u32 cmd, void __user *useraddr)
{
struct ethtool_rxnfc info;
size_t info_size = sizeof(info);
const struct ethtool_ops *ops = dev->ethtool_ops;
int ret;
if (!ops->get_rxnfc && !ops->get_rxfh_fields)
return -EOPNOTSUPP;
ret = ethtool_rxnfc_copy_struct(cmd, &info, &info_size, useraddr);
if (ret)
return ret;
if (info.flow_type & FLOW_RSS && info.rss_context &&
!ops->rxfh_per_ctx_fields)
return -EINVAL;
if (ops->get_rxfh_fields) {
struct ethtool_rxfh_fields fields = {
.flow_type = info.flow_type & ~FLOW_RSS,
};
if (info.flow_type & FLOW_RSS)
fields.rss_context = info.rss_context;
ret = ops->get_rxfh_fields(dev, &fields);
if (ret < 0)
return ret;
info.data = fields.data;
} else {
ret = ops->get_rxnfc(dev, &info, NULL);
if (ret < 0)
return ret;
}
return ethtool_rxnfc_copy_to_user(useraddr, &info, info_size, NULL);
}
static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev,
u32 cmd, void __user *useraddr)
{
@ -1088,18 +1183,6 @@ static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev,
return -EINVAL;
}
if (cmd == ETHTOOL_SRXFH && ops->get_rxfh) {
struct ethtool_rxfh_param rxfh = {};
rc = ops->get_rxfh(dev, &rxfh);
if (rc)
return rc;
rc = ethtool_check_xfrm_rxfh(rxfh.input_xfrm, info.data);
if (rc)
return rc;
}
rc = ops->set_rxnfc(dev, &info);
if (rc)
return rc;
@ -1445,7 +1528,7 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
u8 *rss_config;
int ret;
if (!ops->get_rxnfc || !ops->set_rxfh)
if ((!ops->get_rxnfc && !ops->get_rxfh_fields) || !ops->set_rxfh)
return -EOPNOTSUPP;
if (ops->get_rxfh_indir_size)
@ -3339,13 +3422,17 @@ __dev_ethtool(struct net *net, struct ifreq *ifr, void __user *useraddr,
dev->ethtool_ops->set_priv_flags);
break;
case ETHTOOL_GRXFH:
rc = ethtool_get_rxfh_fields(dev, ethcmd, useraddr);
break;
case ETHTOOL_SRXFH:
rc = ethtool_set_rxfh_fields(dev, ethcmd, useraddr);
break;
case ETHTOOL_GRXRINGS:
case ETHTOOL_GRXCLSRLCNT:
case ETHTOOL_GRXCLSRULE:
case ETHTOOL_GRXCLSRLALL:
rc = ethtool_get_rxnfc(dev, ethcmd, useraddr);
break;
case ETHTOOL_SRXFH:
case ETHTOOL_SRXCLSRLDEL:
case ETHTOOL_SRXCLSRLINS:
rc = ethtool_set_rxnfc(dev, ethcmd, useraddr);