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
* 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;
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,
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)
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)
+ if (!ops->set_rxnfc && !ops->set_rxfh_fields)
return -EOPNOTSUPP;
rc = ethtool_rxnfc_copy_struct(cmd, &info, &info_size, useraddr);
return rc;
}
- return ops->set_rxnfc(dev, &info);
+ 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
const struct ethtool_ops *ops = dev->ethtool_ops;
int ret;
- if (!ops->get_rxnfc)
+ if (!ops->get_rxnfc && !ops->get_rxfh_fields)
return -EOPNOTSUPP;
ret = ethtool_rxnfc_copy_struct(cmd, &info, &info_size, useraddr);
!ops->rxfh_per_ctx_fields)
return -EINVAL;
- ret = ops->get_rxnfc(dev, &info, NULL);
- if (ret < 0)
- return ret;
+ 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);
}
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)