Commit 17f780b3 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'dpaa2-eth-Add-flow-steering-support-without-masking'



Ioana Ciocoi Radulescu says:

====================
dpaa2-eth: Add flow steering support without masking

On DPAA2 platforms that lack a TCAM (like LS1088A), masking of
flow steering keys is not supported. Until now we didn't offer
flow steering capabilities at all on these platforms.

Introduce a limited support for flow steering, where we only
allow ethtool rules that share a common key (i.e. have the same
header fields). If a rule with a new composition key is wanted,
the user must first manually delete all previous rules.

First patch fixes a minor bug, the next two cleanup and prepare
the code and the last one introduces the actual FS support.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 9bad65e5 2d680237
Loading
Loading
Loading
Loading
+66 −14
Original line number Diff line number Diff line
@@ -2571,10 +2571,12 @@ static const struct dpaa2_eth_dist_fields dist_fields[] = {
		.rxnfc_field = RXH_L2DA,
		.cls_prot = NET_PROT_ETH,
		.cls_field = NH_FLD_ETH_DA,
		.id = DPAA2_ETH_DIST_ETHDST,
		.size = 6,
	}, {
		.cls_prot = NET_PROT_ETH,
		.cls_field = NH_FLD_ETH_SA,
		.id = DPAA2_ETH_DIST_ETHSRC,
		.size = 6,
	}, {
		/* This is the last ethertype field parsed:
@@ -2583,28 +2585,33 @@ static const struct dpaa2_eth_dist_fields dist_fields[] = {
		 */
		.cls_prot = NET_PROT_ETH,
		.cls_field = NH_FLD_ETH_TYPE,
		.id = DPAA2_ETH_DIST_ETHTYPE,
		.size = 2,
	}, {
		/* VLAN header */
		.rxnfc_field = RXH_VLAN,
		.cls_prot = NET_PROT_VLAN,
		.cls_field = NH_FLD_VLAN_TCI,
		.id = DPAA2_ETH_DIST_VLAN,
		.size = 2,
	}, {
		/* IP header */
		.rxnfc_field = RXH_IP_SRC,
		.cls_prot = NET_PROT_IP,
		.cls_field = NH_FLD_IP_SRC,
		.id = DPAA2_ETH_DIST_IPSRC,
		.size = 4,
	}, {
		.rxnfc_field = RXH_IP_DST,
		.cls_prot = NET_PROT_IP,
		.cls_field = NH_FLD_IP_DST,
		.id = DPAA2_ETH_DIST_IPDST,
		.size = 4,
	}, {
		.rxnfc_field = RXH_L3_PROTO,
		.cls_prot = NET_PROT_IP,
		.cls_field = NH_FLD_IP_PROTO,
		.id = DPAA2_ETH_DIST_IPPROTO,
		.size = 1,
	}, {
		/* Using UDP ports, this is functionally equivalent to raw
@@ -2613,11 +2620,13 @@ static const struct dpaa2_eth_dist_fields dist_fields[] = {
		.rxnfc_field = RXH_L4_B_0_1,
		.cls_prot = NET_PROT_UDP,
		.cls_field = NH_FLD_UDP_PORT_SRC,
		.id = DPAA2_ETH_DIST_L4SRC,
		.size = 2,
	}, {
		.rxnfc_field = RXH_L4_B_2_3,
		.cls_prot = NET_PROT_UDP,
		.cls_field = NH_FLD_UDP_PORT_DST,
		.id = DPAA2_ETH_DIST_L4DST,
		.size = 2,
	},
};
@@ -2683,12 +2692,15 @@ static int config_cls_key(struct dpaa2_eth_priv *priv, dma_addr_t key)
}

/* Size of the Rx flow classification key */
int dpaa2_eth_cls_key_size(void)
int dpaa2_eth_cls_key_size(u64 fields)
{
	int i, size = 0;

	for (i = 0; i < ARRAY_SIZE(dist_fields); i++)
	for (i = 0; i < ARRAY_SIZE(dist_fields); i++) {
		if (!(fields & dist_fields[i].id))
			continue;
		size += dist_fields[i].size;
	}

	return size;
}
@@ -2709,6 +2721,24 @@ int dpaa2_eth_cls_fld_off(int prot, int field)
	return 0;
}

/* Prune unused fields from the classification rule.
 * Used when masking is not supported
 */
void dpaa2_eth_cls_trim_rule(void *key_mem, u64 fields)
{
	int off = 0, new_off = 0;
	int i, size;

	for (i = 0; i < ARRAY_SIZE(dist_fields); i++) {
		size = dist_fields[i].size;
		if (dist_fields[i].id & fields) {
			memcpy(key_mem + new_off, key_mem + off, size);
			new_off += size;
		}
		off += size;
	}
}

/* Set Rx distribution (hash or flow classification) key
 * flags is a combination of RXH_ bits
 */
@@ -2730,14 +2760,13 @@ static int dpaa2_eth_set_dist_key(struct net_device *net_dev,
		struct dpkg_extract *key =
			&cls_cfg.extracts[cls_cfg.num_extracts];

		/* For Rx hashing key we set only the selected fields.
		 * For Rx flow classification key we set all supported fields
		/* For both Rx hashing and classification keys
		 * we set only the selected fields.
		 */
		if (type == DPAA2_ETH_RX_DIST_HASH) {
			if (!(flags & dist_fields[i].rxnfc_field))
		if (!(flags & dist_fields[i].id))
			continue;
		if (type == DPAA2_ETH_RX_DIST_HASH)
			rx_hash_fields |= dist_fields[i].rxnfc_field;
		}

		if (cls_cfg.num_extracts >= DPKG_MAX_NUM_OF_EXTRACTS) {
			dev_err(dev, "error adding key extraction rule, too many rules?\n");
@@ -2792,16 +2821,28 @@ free_key:
int dpaa2_eth_set_hash(struct net_device *net_dev, u64 flags)
{
	struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
	u64 key = 0;
	int i;

	if (!dpaa2_eth_hash_enabled(priv))
		return -EOPNOTSUPP;

	return dpaa2_eth_set_dist_key(net_dev, DPAA2_ETH_RX_DIST_HASH, flags);
	for (i = 0; i < ARRAY_SIZE(dist_fields); i++)
		if (dist_fields[i].rxnfc_field & flags)
			key |= dist_fields[i].id;

	return dpaa2_eth_set_dist_key(net_dev, DPAA2_ETH_RX_DIST_HASH, key);
}

static int dpaa2_eth_set_cls(struct dpaa2_eth_priv *priv)
int dpaa2_eth_set_cls(struct net_device *net_dev, u64 flags)
{
	return dpaa2_eth_set_dist_key(net_dev, DPAA2_ETH_RX_DIST_CLS, flags);
}

static int dpaa2_eth_set_default_cls(struct dpaa2_eth_priv *priv)
{
	struct device *dev = priv->net_dev->dev.parent;
	int err;

	/* Check if we actually support Rx flow classification */
	if (dpaa2_eth_has_legacy_dist(priv)) {
@@ -2809,8 +2850,7 @@ static int dpaa2_eth_set_cls(struct dpaa2_eth_priv *priv)
		return -EOPNOTSUPP;
	}

	if (priv->dpni_attrs.options & DPNI_OPT_NO_FS ||
	    !(priv->dpni_attrs.options & DPNI_OPT_HAS_KEY_MASKING)) {
	if (!dpaa2_eth_fs_enabled(priv)) {
		dev_dbg(dev, "Rx cls disabled in DPNI options\n");
		return -EOPNOTSUPP;
	}
@@ -2820,9 +2860,21 @@ static int dpaa2_eth_set_cls(struct dpaa2_eth_priv *priv)
		return -EOPNOTSUPP;
	}

	/* If there is no support for masking in the classification table,
	 * we don't set a default key, as it will depend on the rules
	 * added by the user at runtime.
	 */
	if (!dpaa2_eth_fs_mask_enabled(priv))
		goto out;

	err = dpaa2_eth_set_cls(priv->net_dev, DPAA2_ETH_DIST_ALL);
	if (err)
		return err;

out:
	priv->rx_cls_enabled = 1;

	return dpaa2_eth_set_dist_key(priv->net_dev, DPAA2_ETH_RX_DIST_CLS, 0);
	return 0;
}

/* Bind the DPNI to its needed objects and resources: buffer pool, DPIOs,
@@ -2857,7 +2909,7 @@ static int bind_dpni(struct dpaa2_eth_priv *priv)
	/* Configure the flow classification key; it includes all
	 * supported header fields and cannot be modified at runtime
	 */
	err = dpaa2_eth_set_cls(priv);
	err = dpaa2_eth_set_default_cls(priv);
	if (err && err != -EOPNOTSUPP)
		dev_err(dev, "Failed to configure Rx classification key\n");

+23 −1
Original line number Diff line number Diff line
@@ -342,6 +342,7 @@ struct dpaa2_eth_dist_fields {
	enum net_prot cls_prot;
	int cls_field;
	int size;
	u64 id;
};

struct dpaa2_eth_cls_rule {
@@ -394,6 +395,7 @@ struct dpaa2_eth_priv {

	/* enabled ethtool hashing bits */
	u64 rx_hash_fields;
	u64 rx_cls_fields;
	struct dpaa2_eth_cls_rule *cls_rules;
	u8 rx_cls_enabled;
	struct bpf_prog *xdp_prog;
@@ -437,6 +439,12 @@ static inline int dpaa2_eth_cmp_dpni_ver(struct dpaa2_eth_priv *priv,
	(dpaa2_eth_cmp_dpni_ver((priv), DPNI_RX_DIST_KEY_VER_MAJOR,	\
				DPNI_RX_DIST_KEY_VER_MINOR) < 0)

#define dpaa2_eth_fs_enabled(priv)	\
	(!((priv)->dpni_attrs.options & DPNI_OPT_NO_FS))

#define dpaa2_eth_fs_mask_enabled(priv)	\
	((priv)->dpni_attrs.options & DPNI_OPT_HAS_KEY_MASKING)

#define dpaa2_eth_fs_count(priv)        \
	((priv)->dpni_attrs.fs_entries)

@@ -449,6 +457,18 @@ enum dpaa2_eth_rx_dist {
	DPAA2_ETH_RX_DIST_CLS
};

/* Unique IDs for the supported Rx classification header fields */
#define DPAA2_ETH_DIST_ETHDST		BIT(0)
#define DPAA2_ETH_DIST_ETHSRC		BIT(1)
#define DPAA2_ETH_DIST_ETHTYPE		BIT(2)
#define DPAA2_ETH_DIST_VLAN		BIT(3)
#define DPAA2_ETH_DIST_IPSRC		BIT(4)
#define DPAA2_ETH_DIST_IPDST		BIT(5)
#define DPAA2_ETH_DIST_IPPROTO		BIT(6)
#define DPAA2_ETH_DIST_L4SRC		BIT(7)
#define DPAA2_ETH_DIST_L4DST		BIT(8)
#define DPAA2_ETH_DIST_ALL		(~0U)

static inline
unsigned int dpaa2_eth_needed_headroom(struct dpaa2_eth_priv *priv,
				       struct sk_buff *skb)
@@ -483,7 +503,9 @@ static inline unsigned int dpaa2_eth_rx_head_room(struct dpaa2_eth_priv *priv)
}

int dpaa2_eth_set_hash(struct net_device *net_dev, u64 flags);
int dpaa2_eth_cls_key_size(void);
int dpaa2_eth_set_cls(struct net_device *net_dev, u64 key);
int dpaa2_eth_cls_key_size(u64 key);
int dpaa2_eth_cls_fld_off(int prot, int field);
void dpaa2_eth_cls_trim_rule(void *key_mem, u64 fields);

#endif	/* __DPAA2_H */
+78 −19
Original line number Diff line number Diff line
@@ -264,7 +264,7 @@ static void dpaa2_eth_get_ethtool_stats(struct net_device *net_dev,
}

static int prep_eth_rule(struct ethhdr *eth_value, struct ethhdr *eth_mask,
			 void *key, void *mask)
			 void *key, void *mask, u64 *fields)
{
	int off;

@@ -272,18 +272,21 @@ static int prep_eth_rule(struct ethhdr *eth_value, struct ethhdr *eth_mask,
		off = dpaa2_eth_cls_fld_off(NET_PROT_ETH, NH_FLD_ETH_TYPE);
		*(__be16 *)(key + off) = eth_value->h_proto;
		*(__be16 *)(mask + off) = eth_mask->h_proto;
		*fields |= DPAA2_ETH_DIST_ETHTYPE;
	}

	if (!is_zero_ether_addr(eth_mask->h_source)) {
		off = dpaa2_eth_cls_fld_off(NET_PROT_ETH, NH_FLD_ETH_SA);
		ether_addr_copy(key + off, eth_value->h_source);
		ether_addr_copy(mask + off, eth_mask->h_source);
		*fields |= DPAA2_ETH_DIST_ETHSRC;
	}

	if (!is_zero_ether_addr(eth_mask->h_dest)) {
		off = dpaa2_eth_cls_fld_off(NET_PROT_ETH, NH_FLD_ETH_DA);
		ether_addr_copy(key + off, eth_value->h_dest);
		ether_addr_copy(mask + off, eth_mask->h_dest);
		*fields |= DPAA2_ETH_DIST_ETHDST;
	}

	return 0;
@@ -291,7 +294,7 @@ static int prep_eth_rule(struct ethhdr *eth_value, struct ethhdr *eth_mask,

static int prep_uip_rule(struct ethtool_usrip4_spec *uip_value,
			 struct ethtool_usrip4_spec *uip_mask,
			 void *key, void *mask)
			 void *key, void *mask, u64 *fields)
{
	int off;
	u32 tmp_value, tmp_mask;
@@ -303,18 +306,21 @@ static int prep_uip_rule(struct ethtool_usrip4_spec *uip_value,
		off = dpaa2_eth_cls_fld_off(NET_PROT_IP, NH_FLD_IP_SRC);
		*(__be32 *)(key + off) = uip_value->ip4src;
		*(__be32 *)(mask + off) = uip_mask->ip4src;
		*fields |= DPAA2_ETH_DIST_IPSRC;
	}

	if (uip_mask->ip4dst) {
		off = dpaa2_eth_cls_fld_off(NET_PROT_IP, NH_FLD_IP_DST);
		*(__be32 *)(key + off) = uip_value->ip4dst;
		*(__be32 *)(mask + off) = uip_mask->ip4dst;
		*fields |= DPAA2_ETH_DIST_IPDST;
	}

	if (uip_mask->proto) {
		off = dpaa2_eth_cls_fld_off(NET_PROT_IP, NH_FLD_IP_PROTO);
		*(u8 *)(key + off) = uip_value->proto;
		*(u8 *)(mask + off) = uip_mask->proto;
		*fields |= DPAA2_ETH_DIST_IPPROTO;
	}

	if (uip_mask->l4_4_bytes) {
@@ -324,23 +330,26 @@ static int prep_uip_rule(struct ethtool_usrip4_spec *uip_value,
		off = dpaa2_eth_cls_fld_off(NET_PROT_UDP, NH_FLD_UDP_PORT_SRC);
		*(__be16 *)(key + off) = htons(tmp_value >> 16);
		*(__be16 *)(mask + off) = htons(tmp_mask >> 16);
		*fields |= DPAA2_ETH_DIST_L4SRC;

		off = dpaa2_eth_cls_fld_off(NET_PROT_UDP, NH_FLD_UDP_PORT_DST);
		*(__be16 *)(key + off) = htons(tmp_value & 0xFFFF);
		*(__be16 *)(mask + off) = htons(tmp_mask & 0xFFFF);
		*fields |= DPAA2_ETH_DIST_L4DST;
	}

	/* Only apply the rule for IPv4 frames */
	off = dpaa2_eth_cls_fld_off(NET_PROT_ETH, NH_FLD_ETH_TYPE);
	*(__be16 *)(key + off) = htons(ETH_P_IP);
	*(__be16 *)(mask + off) = htons(0xFFFF);
	*fields |= DPAA2_ETH_DIST_ETHTYPE;

	return 0;
}

static int prep_l4_rule(struct ethtool_tcpip4_spec *l4_value,
			struct ethtool_tcpip4_spec *l4_mask,
			void *key, void *mask, u8 l4_proto)
			void *key, void *mask, u8 l4_proto, u64 *fields)
{
	int off;

@@ -351,41 +360,47 @@ static int prep_l4_rule(struct ethtool_tcpip4_spec *l4_value,
		off = dpaa2_eth_cls_fld_off(NET_PROT_IP, NH_FLD_IP_SRC);
		*(__be32 *)(key + off) = l4_value->ip4src;
		*(__be32 *)(mask + off) = l4_mask->ip4src;
		*fields |= DPAA2_ETH_DIST_IPSRC;
	}

	if (l4_mask->ip4dst) {
		off = dpaa2_eth_cls_fld_off(NET_PROT_IP, NH_FLD_IP_DST);
		*(__be32 *)(key + off) = l4_value->ip4dst;
		*(__be32 *)(mask + off) = l4_mask->ip4dst;
		*fields |= DPAA2_ETH_DIST_IPDST;
	}

	if (l4_mask->psrc) {
		off = dpaa2_eth_cls_fld_off(NET_PROT_UDP, NH_FLD_UDP_PORT_SRC);
		*(__be16 *)(key + off) = l4_value->psrc;
		*(__be16 *)(mask + off) = l4_mask->psrc;
		*fields |= DPAA2_ETH_DIST_L4SRC;
	}

	if (l4_mask->pdst) {
		off = dpaa2_eth_cls_fld_off(NET_PROT_UDP, NH_FLD_UDP_PORT_DST);
		*(__be16 *)(key + off) = l4_value->pdst;
		*(__be16 *)(mask + off) = l4_mask->pdst;
		*fields |= DPAA2_ETH_DIST_L4DST;
	}

	/* Only apply the rule for IPv4 frames with the specified L4 proto */
	off = dpaa2_eth_cls_fld_off(NET_PROT_ETH, NH_FLD_ETH_TYPE);
	*(__be16 *)(key + off) = htons(ETH_P_IP);
	*(__be16 *)(mask + off) = htons(0xFFFF);
	*fields |= DPAA2_ETH_DIST_ETHTYPE;

	off = dpaa2_eth_cls_fld_off(NET_PROT_IP, NH_FLD_IP_PROTO);
	*(u8 *)(key + off) = l4_proto;
	*(u8 *)(mask + off) = 0xFF;
	*fields |= DPAA2_ETH_DIST_IPPROTO;

	return 0;
}

static int prep_ext_rule(struct ethtool_flow_ext *ext_value,
			 struct ethtool_flow_ext *ext_mask,
			 void *key, void *mask)
			 void *key, void *mask, u64 *fields)
{
	int off;

@@ -396,6 +411,7 @@ static int prep_ext_rule(struct ethtool_flow_ext *ext_value,
		off = dpaa2_eth_cls_fld_off(NET_PROT_VLAN, NH_FLD_VLAN_TCI);
		*(__be16 *)(key + off) = ext_value->vlan_tci;
		*(__be16 *)(mask + off) = ext_mask->vlan_tci;
		*fields |= DPAA2_ETH_DIST_VLAN;
	}

	return 0;
@@ -403,7 +419,7 @@ static int prep_ext_rule(struct ethtool_flow_ext *ext_value,

static int prep_mac_ext_rule(struct ethtool_flow_ext *ext_value,
			     struct ethtool_flow_ext *ext_mask,
			     void *key, void *mask)
			     void *key, void *mask, u64 *fields)
{
	int off;

@@ -411,36 +427,38 @@ static int prep_mac_ext_rule(struct ethtool_flow_ext *ext_value,
		off = dpaa2_eth_cls_fld_off(NET_PROT_ETH, NH_FLD_ETH_DA);
		ether_addr_copy(key + off, ext_value->h_dest);
		ether_addr_copy(mask + off, ext_mask->h_dest);
		*fields |= DPAA2_ETH_DIST_ETHDST;
	}

	return 0;
}

static int prep_cls_rule(struct ethtool_rx_flow_spec *fs, void *key, void *mask)
static int prep_cls_rule(struct ethtool_rx_flow_spec *fs, void *key, void *mask,
			 u64 *fields)
{
	int err;

	switch (fs->flow_type & 0xFF) {
	case ETHER_FLOW:
		err = prep_eth_rule(&fs->h_u.ether_spec, &fs->m_u.ether_spec,
				    key, mask);
				    key, mask, fields);
		break;
	case IP_USER_FLOW:
		err = prep_uip_rule(&fs->h_u.usr_ip4_spec,
				    &fs->m_u.usr_ip4_spec, key, mask);
				    &fs->m_u.usr_ip4_spec, key, mask, fields);
		break;
	case TCP_V4_FLOW:
		err = prep_l4_rule(&fs->h_u.tcp_ip4_spec, &fs->m_u.tcp_ip4_spec,
				   key, mask, IPPROTO_TCP);
				   key, mask, IPPROTO_TCP, fields);
		break;
	case UDP_V4_FLOW:
		err = prep_l4_rule(&fs->h_u.udp_ip4_spec, &fs->m_u.udp_ip4_spec,
				   key, mask, IPPROTO_UDP);
				   key, mask, IPPROTO_UDP, fields);
		break;
	case SCTP_V4_FLOW:
		err = prep_l4_rule(&fs->h_u.sctp_ip4_spec,
				   &fs->m_u.sctp_ip4_spec, key, mask,
				   IPPROTO_SCTP);
				   IPPROTO_SCTP, fields);
		break;
	default:
		return -EOPNOTSUPP;
@@ -450,13 +468,14 @@ static int prep_cls_rule(struct ethtool_rx_flow_spec *fs, void *key, void *mask)
		return err;

	if (fs->flow_type & FLOW_EXT) {
		err = prep_ext_rule(&fs->h_ext, &fs->m_ext, key, mask);
		err = prep_ext_rule(&fs->h_ext, &fs->m_ext, key, mask, fields);
		if (err)
			return err;
	}

	if (fs->flow_type & FLOW_MAC_EXT) {
		err = prep_mac_ext_rule(&fs->h_ext, &fs->m_ext, key, mask);
		err = prep_mac_ext_rule(&fs->h_ext, &fs->m_ext, key, mask,
					fields);
		if (err)
			return err;
	}
@@ -473,6 +492,7 @@ static int do_cls_rule(struct net_device *net_dev,
	struct dpni_rule_cfg rule_cfg = { 0 };
	struct dpni_fs_action_cfg fs_act = { 0 };
	dma_addr_t key_iova;
	u64 fields = 0;
	void *key_buf;
	int err;

@@ -480,7 +500,7 @@ static int do_cls_rule(struct net_device *net_dev,
	    fs->ring_cookie >= dpaa2_eth_queue_count(priv))
		return -EINVAL;

	rule_cfg.key_size = dpaa2_eth_cls_key_size();
	rule_cfg.key_size = dpaa2_eth_cls_key_size(DPAA2_ETH_DIST_ALL);

	/* allocate twice the key size, for the actual key and for mask */
	key_buf = kzalloc(rule_cfg.key_size * 2, GFP_KERNEL);
@@ -488,10 +508,36 @@ static int do_cls_rule(struct net_device *net_dev,
		return -ENOMEM;

	/* Fill the key and mask memory areas */
	err = prep_cls_rule(fs, key_buf, key_buf + rule_cfg.key_size);
	err = prep_cls_rule(fs, key_buf, key_buf + rule_cfg.key_size, &fields);
	if (err)
		goto free_mem;

	if (!dpaa2_eth_fs_mask_enabled(priv)) {
		/* Masking allows us to configure a maximal key during init and
		 * use it for all flow steering rules. Without it, we include
		 * in the key only the fields actually used, so we need to
		 * extract the others from the final key buffer.
		 *
		 * Program the FS key if needed, or return error if previously
		 * set key can't be used for the current rule. User needs to
		 * delete existing rules in this case to allow for the new one.
		 */
		if (!priv->rx_cls_fields) {
			err = dpaa2_eth_set_cls(net_dev, fields);
			if (err)
				goto free_mem;

			priv->rx_cls_fields = fields;
		} else if (priv->rx_cls_fields != fields) {
			netdev_err(net_dev, "No support for multiple FS keys, need to delete existing rules\n");
			err = -EOPNOTSUPP;
			goto free_mem;
		}

		dpaa2_eth_cls_trim_rule(key_buf, fields);
		rule_cfg.key_size = dpaa2_eth_cls_key_size(fields);
	}

	key_iova = dma_map_single(dev, key_buf, rule_cfg.key_size * 2,
				  DMA_TO_DEVICE);
	if (dma_mapping_error(dev, key_iova)) {
@@ -500,6 +546,7 @@ static int do_cls_rule(struct net_device *net_dev,
	}

	rule_cfg.key_iova = key_iova;
	if (dpaa2_eth_fs_mask_enabled(priv))
		rule_cfg.mask_iova = key_iova + rule_cfg.key_size;

	if (add) {
@@ -522,6 +569,17 @@ free_mem:
	return err;
}

static int num_rules(struct dpaa2_eth_priv *priv)
{
	int i, rules = 0;

	for (i = 0; i < dpaa2_eth_fs_count(priv); i++)
		if (priv->cls_rules[i].in_use)
			rules++;

	return rules;
}

static int update_cls_rule(struct net_device *net_dev,
			   struct ethtool_rx_flow_spec *new_fs,
			   int location)
@@ -545,6 +603,9 @@ static int update_cls_rule(struct net_device *net_dev,
			return err;

		rule->in_use = 0;

		if (!dpaa2_eth_fs_mask_enabled(priv) && !num_rules(priv))
			priv->rx_cls_fields = 0;
	}

	/* If no new entry to add, return here */
@@ -581,9 +642,7 @@ static int dpaa2_eth_get_rxnfc(struct net_device *net_dev,
		break;
	case ETHTOOL_GRXCLSRLCNT:
		rxnfc->rule_cnt = 0;
		for (i = 0; i < max_rules; i++)
			if (priv->cls_rules[i].in_use)
				rxnfc->rule_cnt++;
		rxnfc->rule_cnt = num_rules(priv);
		rxnfc->data = max_rules;
		break;
	case ETHTOOL_GRXCLSRULE: