1

net: ethtool: add support for symmetric-xor RSS hash

Symmetric RSS hash functions are beneficial in applications that monitor
both Tx and Rx packets of the same flow (IDS, software firewalls, ..etc).
Getting all traffic of the same flow on the same RX queue results in
higher CPU cache efficiency.

A NIC that supports "symmetric-xor" can achieve this RSS hash symmetry
by XORing the source and destination fields and pass the values to the
RSS hash algorithm.

The user may request RSS hash symmetry for a specific algorithm, via:

    # ethtool -X eth0 hfunc <hash_alg> symmetric-xor

or turn symmetry off (asymmetric) by:

    # ethtool -X eth0 hfunc <hash_alg>

The specific fields for each flow type should then be specified as usual
via:
    # ethtool -N|-U eth0 rx-flow-hash <flow_type> s|d|f|n

Reviewed-by: Wojciech Drewek <wojciech.drewek@intel.com>
Signed-off-by: Ahmed Zaki <ahmed.zaki@intel.com>
Link: https://lore.kernel.org/r/20231213003321.605376-4-ahmed.zaki@intel.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Ahmed Zaki 2023-12-12 17:33:16 -07:00 committed by Jakub Kicinski
parent dcd8dbf9e7
commit 13e59344fb
8 changed files with 74 additions and 6 deletions

View File

@ -908,6 +908,9 @@ attribute-sets:
- -
name: hkey name: hkey
type: binary type: binary
-
name: input_xfrm
type: u32
- -
name: plca name: plca
attributes: attributes:
@ -1598,6 +1601,7 @@ operations:
- hfunc - hfunc
- indir - indir
- hkey - hkey
- input_xfrm
dump: *rss-get-op dump: *rss-get-op
- -
name: plca-get-cfg name: plca-get-cfg

View File

@ -1774,12 +1774,16 @@ Kernel response contents:
``ETHTOOL_A_RSS_HFUNC`` u32 RSS hash func ``ETHTOOL_A_RSS_HFUNC`` u32 RSS hash func
``ETHTOOL_A_RSS_INDIR`` binary Indir table bytes ``ETHTOOL_A_RSS_INDIR`` binary Indir table bytes
``ETHTOOL_A_RSS_HKEY`` binary Hash key bytes ``ETHTOOL_A_RSS_HKEY`` binary Hash key bytes
``ETHTOOL_A_RSS_INPUT_XFRM`` u32 RSS input data transformation
===================================== ====== ========================== ===================================== ====== ==========================
ETHTOOL_A_RSS_HFUNC attribute is bitmap indicating the hash function ETHTOOL_A_RSS_HFUNC attribute is bitmap indicating the hash function
being used. Current supported options are toeplitz, xor or crc32. being used. Current supported options are toeplitz, xor or crc32.
ETHTOOL_A_RSS_INDIR attribute returns RSS indrection table where each byte ETHTOOL_A_RSS_INDIR attribute returns RSS indirection table where each byte
indicates queue number. indicates queue number.
ETHTOOL_A_RSS_INPUT_XFRM attribute is a bitmap indicating the type of
transformation applied to the input protocol fields before given to the RSS
hfunc. Current supported option is symmetric-xor.
PLCA_GET_CFG PLCA_GET_CFG
============ ============

View File

@ -44,6 +44,21 @@ by masking out the low order seven bits of the computed hash for the
packet (usually a Toeplitz hash), taking this number as a key into the packet (usually a Toeplitz hash), taking this number as a key into the
indirection table and reading the corresponding value. indirection table and reading the corresponding value.
Some NICs support symmetric RSS hashing where, if the IP (source address,
destination address) and TCP/UDP (source port, destination port) tuples
are swapped, the computed hash is the same. This is beneficial in some
applications that monitor TCP/IP flows (IDS, firewalls, ...etc) and need
both directions of the flow to land on the same Rx queue (and CPU). The
"Symmetric-XOR" is a type of RSS algorithms that achieves this hash
symmetry by XORing the input source and destination fields of the IP
and/or L4 protocols. This, however, results in reduced input entropy and
could potentially be exploited. Specifically, the algorithm XORs the input
as follows::
# (SRC_IP ^ DST_IP, SRC_IP ^ DST_IP, SRC_PORT ^ DST_PORT, SRC_PORT ^ DST_PORT)
The result is then fed to the underlying RSS algorithm.
Some advanced NICs allow steering packets to queues based on Some advanced NICs allow steering packets to queues based on
programmable filters. For example, webserver bound TCP port 80 packets programmable filters. For example, webserver bound TCP port 80 packets
can be directed to their own receive queue. Such “n-tuple” filters can can be directed to their own receive queue. Such “n-tuple” filters can

View File

@ -615,6 +615,8 @@ struct ethtool_mm_stats {
* to allocate a new RSS context; on return this field will * to allocate a new RSS context; on return this field will
* contain the ID of the newly allocated context. * contain the ID of the newly allocated context.
* @rss_delete: Set to non-ZERO to remove the @rss_context context. * @rss_delete: Set to non-ZERO to remove the @rss_context context.
* @input_xfrm: Defines how the input data is transformed. Valid values are one
* of %RXH_XFRM_*.
*/ */
struct ethtool_rxfh_param { struct ethtool_rxfh_param {
u8 hfunc; u8 hfunc;
@ -624,6 +626,7 @@ struct ethtool_rxfh_param {
u8 *key; u8 *key;
u32 rss_context; u32 rss_context;
u8 rss_delete; u8 rss_delete;
u8 input_xfrm;
}; };
/** /**
@ -632,6 +635,8 @@ struct ethtool_rxfh_param {
* parameter. * parameter.
* @cap_rss_ctx_supported: indicates if the driver supports RSS * @cap_rss_ctx_supported: indicates if the driver supports RSS
* contexts. * contexts.
* @cap_rss_sym_xor_supported: indicates if the driver supports symmetric-xor
* RSS.
* @supported_coalesce_params: supported types of interrupt coalescing. * @supported_coalesce_params: supported types of interrupt coalescing.
* @supported_ring_params: supported ring params. * @supported_ring_params: supported ring params.
* @get_drvinfo: Report driver/device information. Modern drivers no * @get_drvinfo: Report driver/device information. Modern drivers no
@ -811,6 +816,7 @@ struct ethtool_rxfh_param {
struct ethtool_ops { struct ethtool_ops {
u32 cap_link_lanes_supported:1; u32 cap_link_lanes_supported:1;
u32 cap_rss_ctx_supported:1; u32 cap_rss_ctx_supported:1;
u32 cap_rss_sym_xor_supported:1;
u32 supported_coalesce_params; u32 supported_coalesce_params;
u32 supported_ring_params; u32 supported_ring_params;
void (*get_drvinfo)(struct net_device *, struct ethtool_drvinfo *); void (*get_drvinfo)(struct net_device *, struct ethtool_drvinfo *);

View File

@ -1266,6 +1266,8 @@ struct ethtool_rxfh_indir {
* hardware hash key. * hardware hash key.
* @hfunc: Defines the current RSS hash function used by HW (or to be set to). * @hfunc: Defines the current RSS hash function used by HW (or to be set to).
* Valid values are one of the %ETH_RSS_HASH_*. * Valid values are one of the %ETH_RSS_HASH_*.
* @input_xfrm: Defines how the input data is transformed. Valid values are one
* of %RXH_XFRM_*.
* @rsvd8: Reserved for future use; see the note on reserved space. * @rsvd8: Reserved for future use; see the note on reserved space.
* @rsvd32: Reserved for future use; see the note on reserved space. * @rsvd32: Reserved for future use; see the note on reserved space.
* @rss_config: RX ring/queue index for each hash value i.e., indirection table * @rss_config: RX ring/queue index for each hash value i.e., indirection table
@ -1285,7 +1287,8 @@ struct ethtool_rxfh {
__u32 indir_size; __u32 indir_size;
__u32 key_size; __u32 key_size;
__u8 hfunc; __u8 hfunc;
__u8 rsvd8[3]; __u8 input_xfrm;
__u8 rsvd8[2];
__u32 rsvd32; __u32 rsvd32;
__u32 rss_config[]; __u32 rss_config[];
}; };
@ -1992,6 +1995,14 @@ static inline int ethtool_validate_duplex(__u8 duplex)
#define WOL_MODE_COUNT 8 #define WOL_MODE_COUNT 8
/* RSS hash function data
* XOR the corresponding source and destination fields of each specified
* protocol. Both copies of the XOR'ed fields are fed into the RSS and RXHASH
* calculation. Note that this XORing reduces the input set entropy and could
* be exploited to reduce the RSS queue spread.
*/
#define RXH_XFRM_SYM_XOR (1 << 0)
/* L2-L4 network traffic flow types */ /* L2-L4 network traffic flow types */
#define TCP_V4_FLOW 0x01 /* hash or spec (tcp_ip4_spec) */ #define TCP_V4_FLOW 0x01 /* hash or spec (tcp_ip4_spec) */
#define UDP_V4_FLOW 0x02 /* hash or spec (udp_ip4_spec) */ #define UDP_V4_FLOW 0x02 /* hash or spec (udp_ip4_spec) */

View File

@ -908,6 +908,7 @@ enum {
ETHTOOL_A_RSS_HFUNC, /* u32 */ ETHTOOL_A_RSS_HFUNC, /* u32 */
ETHTOOL_A_RSS_INDIR, /* binary */ ETHTOOL_A_RSS_INDIR, /* binary */
ETHTOOL_A_RSS_HKEY, /* binary */ ETHTOOL_A_RSS_HKEY, /* binary */
ETHTOOL_A_RSS_INPUT_XFRM, /* u32 */
__ETHTOOL_A_RSS_CNT, __ETHTOOL_A_RSS_CNT,
ETHTOOL_A_RSS_MAX = (__ETHTOOL_A_RSS_CNT - 1), ETHTOOL_A_RSS_MAX = (__ETHTOOL_A_RSS_CNT - 1),

View File

@ -972,18 +972,35 @@ static int ethtool_rxnfc_copy_to_user(void __user *useraddr,
static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev, static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev,
u32 cmd, void __user *useraddr) u32 cmd, void __user *useraddr)
{ {
const struct ethtool_ops *ops = dev->ethtool_ops;
struct ethtool_rxfh_param rxfh = {};
struct ethtool_rxnfc info; struct ethtool_rxnfc info;
size_t info_size = sizeof(info); size_t info_size = sizeof(info);
int rc; int rc;
if (!dev->ethtool_ops->set_rxnfc) if (!ops->set_rxnfc || !ops->get_rxfh)
return -EOPNOTSUPP; return -EOPNOTSUPP;
rc = ethtool_rxnfc_copy_struct(cmd, &info, &info_size, useraddr); rc = ethtool_rxnfc_copy_struct(cmd, &info, &info_size, useraddr);
if (rc) if (rc)
return rc; return rc;
rc = dev->ethtool_ops->set_rxnfc(dev, &info); rc = ops->get_rxfh(dev, &rxfh);
if (rc)
return rc;
/* Sanity check: if symmetric-xor is set, then:
* 1 - no other fields besides IP src/dst and/or L4 src/dst
* 2 - If src is set, dst must also be set
*/
if ((rxfh.input_xfrm & RXH_XFRM_SYM_XOR) &&
((info.data & ~(RXH_IP_SRC | RXH_IP_DST |
RXH_L4_B_0_1 | RXH_L4_B_2_3)) ||
(!!(info.data & RXH_IP_SRC) ^ !!(info.data & RXH_IP_DST)) ||
(!!(info.data & RXH_L4_B_0_1) ^ !!(info.data & RXH_L4_B_2_3))))
return -EINVAL;
rc = ops->set_rxnfc(dev, &info);
if (rc) if (rc)
return rc; return rc;
@ -1198,7 +1215,7 @@ static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev,
user_key_size = rxfh.key_size; user_key_size = rxfh.key_size;
/* Check that reserved fields are 0 for now */ /* Check that reserved fields are 0 for now */
if (rxfh.rsvd8[0] || rxfh.rsvd8[1] || rxfh.rsvd8[2] || rxfh.rsvd32) if (rxfh.rsvd8[0] || rxfh.rsvd8[1] || rxfh.rsvd32)
return -EINVAL; return -EINVAL;
/* Most drivers don't handle rss_context, check it's 0 as well */ /* Most drivers don't handle rss_context, check it's 0 as well */
if (rxfh.rss_context && !ops->cap_rss_ctx_supported) if (rxfh.rss_context && !ops->cap_rss_ctx_supported)
@ -1271,11 +1288,15 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
return -EFAULT; return -EFAULT;
/* Check that reserved fields are 0 for now */ /* Check that reserved fields are 0 for now */
if (rxfh.rsvd8[0] || rxfh.rsvd8[1] || rxfh.rsvd8[2] || rxfh.rsvd32) if (rxfh.rsvd8[0] || rxfh.rsvd8[1] || rxfh.rsvd32)
return -EINVAL; return -EINVAL;
/* Most drivers don't handle rss_context, check it's 0 as well */ /* Most drivers don't handle rss_context, check it's 0 as well */
if (rxfh.rss_context && !ops->cap_rss_ctx_supported) if (rxfh.rss_context && !ops->cap_rss_ctx_supported)
return -EOPNOTSUPP; return -EOPNOTSUPP;
/* Check input data transformation capabilities */
if ((rxfh.input_xfrm & RXH_XFRM_SYM_XOR) &&
!ops->cap_rss_sym_xor_supported)
return -EOPNOTSUPP;
/* If either indir, hash key or function is valid, proceed further. /* If either indir, hash key or function is valid, proceed further.
* Must request at least one change: indir size, hash key or function. * Must request at least one change: indir size, hash key or function.
@ -1341,6 +1362,7 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
rxfh_dev.hfunc = rxfh.hfunc; rxfh_dev.hfunc = rxfh.hfunc;
rxfh_dev.rss_context = rxfh.rss_context; rxfh_dev.rss_context = rxfh.rss_context;
rxfh_dev.input_xfrm = rxfh.input_xfrm;
ret = ops->set_rxfh(dev, &rxfh_dev, extack); ret = ops->set_rxfh(dev, &rxfh_dev, extack);
if (ret) if (ret)

View File

@ -13,6 +13,7 @@ struct rss_reply_data {
u32 indir_size; u32 indir_size;
u32 hkey_size; u32 hkey_size;
u32 hfunc; u32 hfunc;
u32 input_xfrm;
u32 *indir_table; u32 *indir_table;
u8 *hkey; u8 *hkey;
}; };
@ -97,6 +98,7 @@ rss_prepare_data(const struct ethnl_req_info *req_base,
goto out_ops; goto out_ops;
data->hfunc = rxfh.hfunc; data->hfunc = rxfh.hfunc;
data->input_xfrm = rxfh.input_xfrm;
out_ops: out_ops:
ethnl_ops_complete(dev); ethnl_ops_complete(dev);
return ret; return ret;
@ -110,6 +112,7 @@ rss_reply_size(const struct ethnl_req_info *req_base,
int len; int len;
len = nla_total_size(sizeof(u32)) + /* _RSS_HFUNC */ len = nla_total_size(sizeof(u32)) + /* _RSS_HFUNC */
nla_total_size(sizeof(u32)) + /* _RSS_INPUT_XFRM */
nla_total_size(sizeof(u32) * data->indir_size) + /* _RSS_INDIR */ nla_total_size(sizeof(u32) * data->indir_size) + /* _RSS_INDIR */
nla_total_size(data->hkey_size); /* _RSS_HKEY */ nla_total_size(data->hkey_size); /* _RSS_HKEY */
@ -124,6 +127,8 @@ rss_fill_reply(struct sk_buff *skb, const struct ethnl_req_info *req_base,
if ((data->hfunc && if ((data->hfunc &&
nla_put_u32(skb, ETHTOOL_A_RSS_HFUNC, data->hfunc)) || nla_put_u32(skb, ETHTOOL_A_RSS_HFUNC, data->hfunc)) ||
(data->input_xfrm &&
nla_put_u32(skb, ETHTOOL_A_RSS_INPUT_XFRM, data->input_xfrm)) ||
(data->indir_size && (data->indir_size &&
nla_put(skb, ETHTOOL_A_RSS_INDIR, nla_put(skb, ETHTOOL_A_RSS_INDIR,
sizeof(u32) * data->indir_size, data->indir_table)) || sizeof(u32) * data->indir_size, data->indir_table)) ||