Commit 352e9bf2 authored by Jeff Guo's avatar Jeff Guo Committed by Jakub Kicinski
Browse files

ice: enable symmetric-xor RSS for Toeplitz hash function



Allow the user to set the symmetric Toeplitz hash function via:

    # ethtool -X eth0 hfunc toeplitz symmetric-xor

All existing RSS configurations will be converted to symmetric unless they
have a non-symmetric field (other than IP src/dst and L4 src/dst ports)
used for hashing. The driver will reject a new RSS configuration if such
a field is requested.

The hash function in the E800 NICs is set per-VSI and a specific AQ
command is needed to modify the hash function. Use the AQ command to
enable setting the symmetric Toeplitz RSS hash function for any VSI
in the new ice_set_rss_hfunc().

When the Symmetric Toeplitz hash function is used, the hardware sets the
input set of the RSS (Toeplitz) algorithm to be the XOR of the fields
index by HSYMM and the fields index by the INSET registers. We use this
to create a symmetric hash by setting the HSYMM registers to point to
their counterparts in the INSET registers:

 HSYMM [src_fv] = dst_fv;
 HSYMM [dst_fv] = src_fv;

where src_fv and dst_fv are the indexes of the protocol's src and dst
fields.

Reviewed-by: default avatarWojciech Drewek <wojciech.drewek@intel.com>
Signed-off-by: default avatarJeff Guo <jia.guo@intel.com>
Signed-off-by: default avatarJesse Brandeburg <jesse.brandeburg@intel.com>
Co-developed-by: default avatarAhmed Zaki <ahmed.zaki@intel.com>
Signed-off-by: default avatarAhmed Zaki <ahmed.zaki@intel.com>
Link: https://lore.kernel.org/r/20231213003321.605376-8-ahmed.zaki@intel.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent b1f5921a
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -360,6 +360,7 @@ struct ice_vsi {
	/* RSS config */
	u16 rss_table_size;	/* HW RSS table size */
	u16 rss_size;		/* Allocated RSS queues */
	u8 rss_hfunc;		/* User configured hash type */
	u8 *rss_hkey_user;	/* User configured hash keys */
	u8 *rss_lut_user;	/* User configured lookup table entries */
	u8 rss_lut_type;	/* used to configure Get/Set RSS LUT AQ call */
@@ -920,6 +921,7 @@ int ice_set_rss_lut(struct ice_vsi *vsi, u8 *lut, u16 lut_size);
int ice_get_rss_lut(struct ice_vsi *vsi, u8 *lut, u16 lut_size);
int ice_set_rss_key(struct ice_vsi *vsi, u8 *seed);
int ice_get_rss_key(struct ice_vsi *vsi, u8 *seed);
int ice_set_rss_hfunc(struct ice_vsi *vsi, u8 hfunc);
void ice_fill_rss_lut(u8 *lut, u16 rss_table_size, u16 rss_size);
int ice_schedule_reset(struct ice_pf *pf, enum ice_reset_req reset);
void ice_print_link_msg(struct ice_vsi *vsi, bool isup);
+1 −0
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@

#include <linux/bitfield.h>

#include "ice.h"
#include "ice_type.h"
#include "ice_nvm.h"
#include "ice_flex_pipe.h"
+22 −17
Original line number Diff line number Diff line
@@ -2502,27 +2502,15 @@ static u32 ice_parse_hdrs(struct ethtool_rxnfc *nfc)
	return hdrs;
}

#define ICE_FLOW_HASH_FLD_IPV4_SA	BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_SA)
#define ICE_FLOW_HASH_FLD_IPV6_SA	BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_SA)
#define ICE_FLOW_HASH_FLD_IPV4_DA	BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_DA)
#define ICE_FLOW_HASH_FLD_IPV6_DA	BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_DA)
#define ICE_FLOW_HASH_FLD_TCP_SRC_PORT	BIT_ULL(ICE_FLOW_FIELD_IDX_TCP_SRC_PORT)
#define ICE_FLOW_HASH_FLD_TCP_DST_PORT	BIT_ULL(ICE_FLOW_FIELD_IDX_TCP_DST_PORT)
#define ICE_FLOW_HASH_FLD_UDP_SRC_PORT	BIT_ULL(ICE_FLOW_FIELD_IDX_UDP_SRC_PORT)
#define ICE_FLOW_HASH_FLD_UDP_DST_PORT	BIT_ULL(ICE_FLOW_FIELD_IDX_UDP_DST_PORT)
#define ICE_FLOW_HASH_FLD_SCTP_SRC_PORT	\
	BIT_ULL(ICE_FLOW_FIELD_IDX_SCTP_SRC_PORT)
#define ICE_FLOW_HASH_FLD_SCTP_DST_PORT	\
	BIT_ULL(ICE_FLOW_FIELD_IDX_SCTP_DST_PORT)

/**
 * ice_parse_hash_flds - parses hash fields from RSS hash input
 * @nfc: ethtool rxnfc command
 * @symm: true if Symmetric Topelitz is set
 *
 * This function parses the rxnfc command and returns intended
 * hash fields for RSS configuration
 */
static u64 ice_parse_hash_flds(struct ethtool_rxnfc *nfc)
static u64 ice_parse_hash_flds(struct ethtool_rxnfc *nfc, bool symm)
{
	u64 hfld = ICE_HASH_INVALID;

@@ -2595,6 +2583,7 @@ ice_set_rss_hash_opt(struct ice_vsi *vsi, struct ethtool_rxnfc *nfc)
	struct device *dev;
	u64 hashed_flds;
	int status;
	bool symm;
	u32 hdrs;

	dev = ice_pf_to_dev(pf);
@@ -2604,7 +2593,8 @@ ice_set_rss_hash_opt(struct ice_vsi *vsi, struct ethtool_rxnfc *nfc)
		return -EINVAL;
	}

	hashed_flds = ice_parse_hash_flds(nfc);
	symm = !!(vsi->rss_hfunc == ICE_AQ_VSI_Q_OPT_RSS_HASH_SYM_TPLZ);
	hashed_flds = ice_parse_hash_flds(nfc, symm);
	if (hashed_flds == ICE_HASH_INVALID) {
		dev_dbg(dev, "Invalid hash fields, vsi num = %d\n",
			vsi->vsi_num);
@@ -2621,7 +2611,9 @@ ice_set_rss_hash_opt(struct ice_vsi *vsi, struct ethtool_rxnfc *nfc)
	cfg.hash_flds = hashed_flds;
	cfg.addl_hdrs = hdrs;
	cfg.hdr_type = ICE_RSS_ANY_HEADERS;
	status = ice_add_rss_cfg(&pf->hw, vsi->idx, &cfg);
	cfg.symm = symm;

	status = ice_add_rss_cfg(&pf->hw, vsi, &cfg);
	if (status) {
		dev_dbg(dev, "ice_add_rss_cfg failed, vsi num = %d, error = %d\n",
			vsi->vsi_num, status);
@@ -2642,6 +2634,7 @@ ice_get_rss_hash_opt(struct ice_vsi *vsi, struct ethtool_rxnfc *nfc)
	struct ice_pf *pf = vsi->back;
	struct device *dev;
	u64 hash_flds;
	bool symm;
	u32 hdrs;

	dev = ice_pf_to_dev(pf);
@@ -2660,7 +2653,7 @@ ice_get_rss_hash_opt(struct ice_vsi *vsi, struct ethtool_rxnfc *nfc)
		return;
	}

	hash_flds = ice_get_rss_cfg(&pf->hw, vsi->idx, hdrs);
	hash_flds = ice_get_rss_cfg(&pf->hw, vsi->idx, hdrs, &symm);
	if (hash_flds == ICE_HASH_INVALID) {
		dev_dbg(dev, "No hash fields found for the given header type, vsi num = %d\n",
			vsi->vsi_num);
@@ -3242,6 +3235,8 @@ ice_get_rxfh(struct net_device *netdev, struct ethtool_rxfh_param *rxfh)
	}

	rxfh->hfunc = ETH_RSS_HASH_TOP;
	if (vsi->rss_hfunc == ICE_AQ_VSI_Q_OPT_RSS_HASH_SYM_TPLZ)
		rxfh->input_xfrm |= RXH_XFRM_SYM_XOR;

	if (!rxfh->indir)
		return 0;
@@ -3286,6 +3281,7 @@ ice_set_rxfh(struct net_device *netdev, struct ethtool_rxfh_param *rxfh,
	     struct netlink_ext_ack *extack)
{
	struct ice_netdev_priv *np = netdev_priv(netdev);
	u8 hfunc = ICE_AQ_VSI_Q_OPT_RSS_HASH_TPLZ;
	struct ice_vsi *vsi = np->vsi;
	struct ice_pf *pf = vsi->back;
	struct device *dev;
@@ -3310,6 +3306,14 @@ ice_set_rxfh(struct net_device *netdev, struct ethtool_rxfh_param *rxfh,
		return -EOPNOTSUPP;
	}

	/* Update the VSI's hash function */
	if (rxfh->input_xfrm & RXH_XFRM_SYM_XOR)
		hfunc = ICE_AQ_VSI_Q_OPT_RSS_HASH_SYM_TPLZ;

	err = ice_set_rss_hfunc(vsi, hfunc);
	if (err)
		return err;

	if (rxfh->key) {
		if (!vsi->rss_hkey_user) {
			vsi->rss_hkey_user =
@@ -4220,6 +4224,7 @@ static const struct ethtool_ops ice_ethtool_ops = {
	.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
				     ETHTOOL_COALESCE_USE_ADAPTIVE |
				     ETHTOOL_COALESCE_RX_USECS_HIGH,
	.cap_rss_sym_xor_supported = true,
	.get_link_ksettings	= ice_get_link_ksettings,
	.set_link_ksettings	= ice_set_link_ksettings,
	.get_drvinfo		= ice_get_drvinfo,
+2 −2
Original line number Diff line number Diff line
@@ -441,7 +441,7 @@ void ice_fdir_replay_flows(struct ice_hw *hw)
			prof = hw->fdir_prof[flow];
			ice_flow_add_prof(hw, ICE_BLK_FD, ICE_FLOW_RX,
					  prof->fdir_seg[tun], TNL_SEG_CNT(tun),
					  &hw_prof);
					  false, &hw_prof);
			for (j = 0; j < prof->cnt; j++) {
				enum ice_flow_priority prio;
				u64 entry_h = 0;
@@ -682,7 +682,7 @@ ice_fdir_set_hw_fltr_rule(struct ice_pf *pf, struct ice_flow_seg_info *seg,
	 * actions (NULL) and zero actions 0.
	 */
	err = ice_flow_add_prof(hw, ICE_BLK_FD, ICE_FLOW_RX, seg,
				TNL_SEG_CNT(tun), &prof);
				TNL_SEG_CNT(tun), false, &prof);
	if (err)
		return err;
	err = ice_flow_add_entry(hw, ICE_BLK_FD, prof->id, main_vsi->idx,
+25 −8
Original line number Diff line number Diff line
@@ -1218,11 +1218,13 @@ ice_prof_has_mask(struct ice_hw *hw, enum ice_block blk, u8 prof, u16 *masks)
 * @blk: HW block
 * @fv: field vector to search for
 * @masks: masks for FV
 * @symm: symmetric setting for RSS flows
 * @prof_id: receives the profile ID
 */
static int
ice_find_prof_id_with_mask(struct ice_hw *hw, enum ice_block blk,
			   struct ice_fv_word *fv, u16 *masks, u8 *prof_id)
			   struct ice_fv_word *fv, u16 *masks, bool symm,
			   u8 *prof_id)
{
	struct ice_es *es = &hw->blk[blk].es;
	u8 i;
@@ -1236,6 +1238,9 @@ ice_find_prof_id_with_mask(struct ice_hw *hw, enum ice_block blk,
	for (i = 0; i < (u8)es->count; i++) {
		u16 off = i * es->fvw;

		if (blk == ICE_BLK_RSS && es->symm[i] != symm)
			continue;

		if (memcmp(&es->t[off], fv, es->fvw * sizeof(*fv)))
			continue;

@@ -1716,15 +1721,16 @@ ice_update_prof_masking(struct ice_hw *hw, enum ice_block blk, u16 prof_id,
}

/**
 * ice_write_es - write an extraction sequence to hardware
 * ice_write_es - write an extraction sequence and symmetric setting to hardware
 * @hw: pointer to the HW struct
 * @blk: the block in which to write the extraction sequence
 * @prof_id: the profile ID to write
 * @fv: pointer to the extraction sequence to write - NULL to clear extraction
 * @symm: symmetric setting for RSS profiles
 */
static void
ice_write_es(struct ice_hw *hw, enum ice_block blk, u8 prof_id,
	     struct ice_fv_word *fv)
	     struct ice_fv_word *fv, bool symm)
{
	u16 off;

@@ -1737,6 +1743,9 @@ ice_write_es(struct ice_hw *hw, enum ice_block blk, u8 prof_id,
		memcpy(&hw->blk[blk].es.t[off], fv,
		       hw->blk[blk].es.fvw * sizeof(*fv));
	}

	if (blk == ICE_BLK_RSS)
		hw->blk[blk].es.symm[prof_id] = symm;
}

/**
@@ -1753,7 +1762,7 @@ ice_prof_dec_ref(struct ice_hw *hw, enum ice_block blk, u8 prof_id)

	if (hw->blk[blk].es.ref_count[prof_id] > 0) {
		if (!--hw->blk[blk].es.ref_count[prof_id]) {
			ice_write_es(hw, blk, prof_id, NULL);
			ice_write_es(hw, blk, prof_id, NULL, false);
			ice_free_prof_masks(hw, blk, prof_id);
			return ice_free_prof_id(hw, blk, prof_id);
		}
@@ -2116,6 +2125,7 @@ void ice_free_hw_tbls(struct ice_hw *hw)
		devm_kfree(ice_hw_to_dev(hw), hw->blk[i].prof_redir.t);
		devm_kfree(ice_hw_to_dev(hw), hw->blk[i].es.t);
		devm_kfree(ice_hw_to_dev(hw), hw->blk[i].es.ref_count);
		devm_kfree(ice_hw_to_dev(hw), hw->blk[i].es.symm);
		devm_kfree(ice_hw_to_dev(hw), hw->blk[i].es.written);
		devm_kfree(ice_hw_to_dev(hw), hw->blk[i].es.mask_ena);
		devm_kfree(ice_hw_to_dev(hw), hw->blk[i].prof_id.id);
@@ -2180,6 +2190,7 @@ void ice_clear_hw_tbls(struct ice_hw *hw)

		memset(es->t, 0, es->count * sizeof(*es->t) * es->fvw);
		memset(es->ref_count, 0, es->count * sizeof(*es->ref_count));
		memset(es->symm, 0, es->count * sizeof(*es->symm));
		memset(es->written, 0, es->count * sizeof(*es->written));
		memset(es->mask_ena, 0, es->count * sizeof(*es->mask_ena));

@@ -2297,6 +2308,11 @@ int ice_init_hw_tbls(struct ice_hw *hw)
		if (!es->ref_count)
			goto err;

		es->symm = devm_kcalloc(ice_hw_to_dev(hw), es->count,
					sizeof(*es->symm), GFP_KERNEL);
		if (!es->symm)
			goto err;

		es->written = devm_kcalloc(ice_hw_to_dev(hw), es->count,
					   sizeof(*es->written), GFP_KERNEL);
		if (!es->written)
@@ -2974,6 +2990,7 @@ ice_add_prof_attrib(struct ice_prof_map *prof, u8 ptg, u16 ptype,
 * @attr_cnt: number of elements in attr array
 * @es: extraction sequence (length of array is determined by the block)
 * @masks: mask for extraction sequence
 * @symm: symmetric setting for RSS profiles
 *
 * This function registers a profile, which matches a set of PTYPES with a
 * particular extraction sequence. While the hardware profile is allocated
@@ -2983,7 +3000,7 @@ ice_add_prof_attrib(struct ice_prof_map *prof, u8 ptg, u16 ptype,
int
ice_add_prof(struct ice_hw *hw, enum ice_block blk, u64 id, u8 ptypes[],
	     const struct ice_ptype_attributes *attr, u16 attr_cnt,
	     struct ice_fv_word *es, u16 *masks)
	     struct ice_fv_word *es, u16 *masks, bool symm)
{
	u32 bytes = DIV_ROUND_UP(ICE_FLOW_PTYPE_MAX, BITS_PER_BYTE);
	DECLARE_BITMAP(ptgs_used, ICE_XLT1_CNT);
@@ -2997,7 +3014,7 @@ ice_add_prof(struct ice_hw *hw, enum ice_block blk, u64 id, u8 ptypes[],
	mutex_lock(&hw->blk[blk].es.prof_map_lock);

	/* search for existing profile */
	status = ice_find_prof_id_with_mask(hw, blk, es, masks, &prof_id);
	status = ice_find_prof_id_with_mask(hw, blk, es, masks, symm, &prof_id);
	if (status) {
		/* allocate profile ID */
		status = ice_alloc_prof_id(hw, blk, &prof_id);
@@ -3020,7 +3037,7 @@ ice_add_prof(struct ice_hw *hw, enum ice_block blk, u64 id, u8 ptypes[],
			goto err_ice_add_prof;

		/* and write new es */
		ice_write_es(hw, blk, prof_id, es);
		ice_write_es(hw, blk, prof_id, es, symm);
	}

	ice_prof_inc_ref(hw, blk, prof_id);
@@ -3108,7 +3125,7 @@ ice_add_prof(struct ice_hw *hw, enum ice_block blk, u64 id, u8 ptypes[],
 * This will search for a profile tracking ID which was previously added.
 * The profile map lock should be held before calling this function.
 */
static struct ice_prof_map *
struct ice_prof_map *
ice_search_prof_id(struct ice_hw *hw, enum ice_block blk, u64 id)
{
	struct ice_prof_map *entry = NULL;
Loading