Subject: [PATCH RFC,net-next 10/10] dsa: bcm_sf2: use flow_rule infrastructure

From: Pablo Neira Ayuso <[email-masked]>
Date: Tue, 25 Sep 2018 21:20:01 +0200

Update this driver to use the flow_rule infrastructure, hence the same
code to populate hardware IR can be used from ethtool_rx_flow and the
cls_flower interfaces.

Signed-off-by: Pablo Neira Ayuso <[email-masked]>
---
 drivers/net/dsa/bcm_sf2_cfp.c | 311 ++++++++++++++++++++++--------------------
 1 file changed, 166 insertions(+), 145 deletions(-)

diff --git a/drivers/net/dsa/bcm_sf2_cfp.c b/drivers/net/dsa/bcm_sf2_cfp.c
index 47c5f272a084..9dace0e25a3a 100644
--- a/drivers/net/dsa/bcm_sf2_cfp.c
+++ b/drivers/net/dsa/bcm_sf2_cfp.c
@@ -251,10 +251,12 @@ static int bcm_sf2_cfp_act_pol_set(struct bcm_sf2_priv *priv,
 }
 
 static void bcm_sf2_cfp_slice_ipv4(struct bcm_sf2_priv *priv,
-				   struct ethtool_tcpip4_spec *v4_spec,
+				   struct flow_rule *flow_rule,
 				   unsigned int slice_num,
 				   bool mask)
 {
+	struct flow_match_ipv4_addrs ipv4;
+	struct flow_match_ports ports;
 	u32 reg, offset;
 
 	/* C-Tag		[31:24]
@@ -268,41 +270,54 @@ static void bcm_sf2_cfp_slice_ipv4(struct bcm_sf2_priv *priv,
 		offset = CORE_CFP_DATA_PORT(4);
 	core_writel(priv, reg, offset);
 
+	flow_rule_match_ipv4_addrs(flow_rule, &ipv4);
+	flow_rule_match_ports(flow_rule, &ports);
+
 	/* UDF_n_A7		[31:24]
 	 * UDF_n_A6		[23:8]
 	 * UDF_n_A5		[7:0]
 	 */
-	reg = be16_to_cpu(v4_spec->pdst) >> 8;
-	if (mask)
+	if (mask) {
+		reg = be16_to_cpu(ports.mask->dst) >> 8;
 		offset = CORE_CFP_MASK_PORT(3);
-	else
+	} else {
+		reg = be16_to_cpu(ports.key->dst) >> 8;
 		offset = CORE_CFP_DATA_PORT(3);
+	}
 	core_writel(priv, reg, offset);
 
 	/* UDF_n_A5		[31:24]
 	 * UDF_n_A4		[23:8]
 	 * UDF_n_A3		[7:0]
 	 */
-	reg = (be16_to_cpu(v4_spec->pdst) & 0xff) << 24 |
-	      (u32)be16_to_cpu(v4_spec->psrc) << 8 |
-	      (be32_to_cpu(v4_spec->ip4dst) & 0x0000ff00) >> 8;
-	if (mask)
+	if (mask) {
+		reg = (be16_to_cpu(ports.mask->dst) & 0xff) << 24 |
+		      (u32)be16_to_cpu(ports.mask->src) << 8 |
+		      (be32_to_cpu(ipv4.mask->dst) & 0x0000ff00) >> 8;
 		offset = CORE_CFP_MASK_PORT(2);
-	else
+	} else {
+		reg = (be16_to_cpu(ports.key->dst) & 0xff) << 24 |
+		      (u32)be16_to_cpu(ports.key->src) << 8 |
+		      (be32_to_cpu(ipv4.key->dst) & 0x0000ff00) >> 8;
 		offset = CORE_CFP_DATA_PORT(2);
+	}
 	core_writel(priv, reg, offset);
 
 	/* UDF_n_A3		[31:24]
 	 * UDF_n_A2		[23:8]
 	 * UDF_n_A1		[7:0]
 	 */
-	reg = (u32)(be32_to_cpu(v4_spec->ip4dst) & 0xff) << 24 |
-	      (u32)(be32_to_cpu(v4_spec->ip4dst) >> 16) << 8 |
-	      (be32_to_cpu(v4_spec->ip4src) & 0x0000ff00) >> 8;
-	if (mask)
+	if (mask) {
+		reg = (u32)(be32_to_cpu(ipv4.mask->dst) & 0xff) << 24 |
+		      (u32)(be32_to_cpu(ipv4.mask->dst) >> 16) << 8 |
+		      (be32_to_cpu(ipv4.mask->src) & 0x0000ff00) >> 8;
 		offset = CORE_CFP_MASK_PORT(1);
-	else
+	} else {
+		reg = (u32)(be32_to_cpu(ipv4.key->dst) & 0xff) << 24 |
+		      (u32)(be32_to_cpu(ipv4.key->dst) >> 16) << 8 |
+		      (be32_to_cpu(ipv4.key->src) & 0x0000ff00) >> 8;
 		offset = CORE_CFP_DATA_PORT(1);
+	}
 	core_writel(priv, reg, offset);
 
 	/* UDF_n_A1		[31:24]
@@ -311,56 +326,34 @@ static void bcm_sf2_cfp_slice_ipv4(struct bcm_sf2_priv *priv,
 	 * Slice ID		[3:2]
 	 * Slice valid		[1:0]
 	 */
-	reg = (u32)(be32_to_cpu(v4_spec->ip4src) & 0xff) << 24 |
-	      (u32)(be32_to_cpu(v4_spec->ip4src) >> 16) << 8 |
-	      SLICE_NUM(slice_num) | SLICE_VALID;
-	if (mask)
+	if (mask) {
+		reg = (u32)(be32_to_cpu(ipv4.mask->src) & 0xff) << 24 |
+		      (u32)(be32_to_cpu(ipv4.mask->src) >> 16) << 8 |
+		      SLICE_NUM(slice_num) | SLICE_VALID;
 		offset = CORE_CFP_MASK_PORT(0);
-	else
+	} else {
+		reg = (u32)(be32_to_cpu(ipv4.key->src) & 0xff) << 24 |
+		      (u32)(be32_to_cpu(ipv4.key->src) >> 16) << 8 |
+		      SLICE_NUM(slice_num) | SLICE_VALID;
 		offset = CORE_CFP_DATA_PORT(0);
+	}
 	core_writel(priv, reg, offset);
 }
 
 static int bcm_sf2_cfp_ipv4_rule_set(struct bcm_sf2_priv *priv, int port,
 				     unsigned int port_num,
 				     unsigned int queue_num,
-				     struct ethtool_rx_flow_spec *fs)
+				     struct flow_rule *flow_rule,
+				     unsigned int rule_index, u8 ip_frag)
 {
-	struct ethtool_tcpip4_spec *v4_spec, *v4_m_spec;
 	const struct cfp_udf_layout *layout;
-	unsigned int slice_num, rule_index;
-	u8 ip_proto, ip_frag;
+	struct flow_match_basic basic;
+	struct flow_match_ip ip;
+	unsigned int slice_num;
 	u8 num_udf;
 	u32 reg;
 	int ret;
 
-	switch (fs->flow_type & ~FLOW_EXT) {
-	case TCP_V4_FLOW:
-		ip_proto = IPPROTO_TCP;
-		v4_spec = &fs->h_u.tcp_ip4_spec;
-		v4_m_spec = &fs->m_u.tcp_ip4_spec;
-		break;
-	case UDP_V4_FLOW:
-		ip_proto = IPPROTO_UDP;
-		v4_spec = &fs->h_u.udp_ip4_spec;
-		v4_m_spec = &fs->m_u.udp_ip4_spec;
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	ip_frag = be32_to_cpu(fs->m_ext.data[0]);
-
-	/* Locate the first rule available */
-	if (fs->location == RX_CLS_LOC_ANY)
-		rule_index = find_first_zero_bit(priv->cfp.used,
-						 priv->num_cfp_rules);
-	else
-		rule_index = fs->location;
-
-	if (rule_index > bcm_sf2_cfp_rule_size(priv))
-		return -ENOSPC;
-
 	layout = &udf_tcpip4_layout;
 	/* We only use one UDF slice for now */
 	slice_num = bcm_sf2_get_slice_number(layout, 0);
@@ -378,6 +371,9 @@ static int bcm_sf2_cfp_ipv4_rule_set(struct bcm_sf2_priv *priv, int port,
 	/* Source port map match */
 	core_writel(priv, 0xff, CORE_CFP_MASK_PORT(7));
 
+	flow_rule_match_basic(flow_rule, &basic);
+	flow_rule_match_ip(flow_rule, &ip);
+
 	/* S-Tag status		[31:30]
 	 * C-Tag status		[29:28]
 	 * L2 framing		[27:26]
@@ -392,8 +388,9 @@ static int bcm_sf2_cfp_ipv4_rule_set(struct bcm_sf2_priv *priv, int port,
 	 * Reserved		[1]
 	 * UDF_Valid[8]		[0]
 	 */
-	core_writel(priv, v4_spec->tos << IPTOS_SHIFT |
-		    ip_proto << IPPROTO_SHIFT | ip_frag << IP_FRAG_SHIFT |
+	core_writel(priv, ip.key->tos << IPTOS_SHIFT |
+		    basic.key->n_proto << IPPROTO_SHIFT |
+		    ip_frag << IP_FRAG_SHIFT |
 		    udf_upper_bits(num_udf),
 		    CORE_CFP_DATA_PORT(6));
 
@@ -411,8 +408,8 @@ static int bcm_sf2_cfp_ipv4_rule_set(struct bcm_sf2_priv *priv, int port,
 	core_writel(priv, udf_lower_bits(num_udf) << 24, CORE_CFP_MASK_PORT(5));
 
 	/* Program the match and the mask */
-	bcm_sf2_cfp_slice_ipv4(priv, v4_spec, slice_num, false);
-	bcm_sf2_cfp_slice_ipv4(priv, v4_m_spec, SLICE_NUM_MASK, true);
+	bcm_sf2_cfp_slice_ipv4(priv, flow_rule, slice_num, false);
+	bcm_sf2_cfp_slice_ipv4(priv, flow_rule, SLICE_NUM_MASK, true);
 
 	/* Insert into TCAM now */
 	bcm_sf2_cfp_rule_addr_set(priv, rule_index);
@@ -437,9 +434,8 @@ static int bcm_sf2_cfp_ipv4_rule_set(struct bcm_sf2_priv *priv, int port,
 	/* Flag the rule as being used and return it */
 	set_bit(rule_index, priv->cfp.used);
 	set_bit(rule_index, priv->cfp.unique);
-	fs->location = rule_index;
 
-	return 0;
+	return rule_index;
 }
 
 static void bcm_sf2_cfp_slice_ipv6(struct bcm_sf2_priv *priv,
@@ -518,33 +514,18 @@ static void bcm_sf2_cfp_slice_ipv6(struct bcm_sf2_priv *priv,
 static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port,
 				     unsigned int port_num,
 				     unsigned int queue_num,
-				     struct ethtool_rx_flow_spec *fs)
+				     struct flow_rule *flow_rule,
+				     unsigned int *rule_index, u8 ip_frag)
 {
-	struct ethtool_tcpip6_spec *v6_spec, *v6_m_spec;
-	unsigned int slice_num, rule_index[2];
 	const struct cfp_udf_layout *layout;
-	u8 ip_proto, ip_frag;
+	struct flow_match_ipv6_addrs ipv6;
+	struct flow_match_ports ports;
+	struct flow_match_basic basic;
+	unsigned int slice_num;
 	int ret = 0;
 	u8 num_udf;
 	u32 reg;
 
-	switch (fs->flow_type & ~FLOW_EXT) {
-	case TCP_V6_FLOW:
-		ip_proto = IPPROTO_TCP;
-		v6_spec = &fs->h_u.tcp_ip6_spec;
-		v6_m_spec = &fs->m_u.tcp_ip6_spec;
-		break;
-	case UDP_V6_FLOW:
-		ip_proto = IPPROTO_UDP;
-		v6_spec = &fs->h_u.udp_ip6_spec;
-		v6_m_spec = &fs->m_u.udp_ip6_spec;
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	ip_frag = be32_to_cpu(fs->m_ext.data[0]);
-
 	layout = &udf_tcpip6_layout;
 	slice_num = bcm_sf2_get_slice_number(layout, 0);
 	if (slice_num == UDF_NUM_SLICES)
@@ -552,38 +533,6 @@ static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port,
 
 	num_udf = bcm_sf2_get_num_udf_slices(layout->udfs[slice_num].slices);
 
-	/* Negotiate two indexes, one for the second half which we are chained
-	 * from, which is what we will return to user-space, and a second one
-	 * which is used to store its first half. That first half does not
-	 * allow any choice of placement, so it just needs to find the next
-	 * available bit. We return the second half as fs->location because
-	 * that helps with the rule lookup later on since the second half is
-	 * chained from its first half, we can easily identify IPv6 CFP rules
-	 * by looking whether they carry a CHAIN_ID.
-	 *
-	 * We also want the second half to have a lower rule_index than its
-	 * first half because the HW search is by incrementing addresses.
-	 */
-	if (fs->location == RX_CLS_LOC_ANY)
-		rule_index[1] = find_first_zero_bit(priv->cfp.used,
-						    priv->num_cfp_rules);
-	else
-		rule_index[1] = fs->location;
-	if (rule_index[1] > bcm_sf2_cfp_rule_size(priv))
-		return -ENOSPC;
-
-	/* Flag it as used (cleared on error path) such that we can immediately
-	 * obtain a second one to chain from.
-	 */
-	set_bit(rule_index[1], priv->cfp.used);
-
-	rule_index[0] = find_first_zero_bit(priv->cfp.used,
-					    priv->num_cfp_rules);
-	if (rule_index[0] > bcm_sf2_cfp_rule_size(priv)) {
-		ret = -ENOSPC;
-		goto out_err;
-	}
-
 	/* Apply the UDF layout for this filter */
 	bcm_sf2_cfp_udf_set(priv, layout, slice_num);
 
@@ -593,6 +542,8 @@ static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port,
 	/* Source port map match */
 	core_writel(priv, 0xff, CORE_CFP_MASK_PORT(7));
 
+	flow_rule_match_basic(flow_rule, &basic);
+
 	/* S-Tag status		[31:30]
 	 * C-Tag status		[29:28]
 	 * L2 framing		[27:26]
@@ -607,7 +558,7 @@ static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port,
 	 * Reserved		[1]
 	 * UDF_Valid[8]		[0]
 	 */
-	reg = 1 << L3_FRAMING_SHIFT | ip_proto << IPPROTO_SHIFT |
+	reg = 1 << L3_FRAMING_SHIFT | basic.key->n_proto << IPPROTO_SHIFT |
 		ip_frag << IP_FRAG_SHIFT | udf_upper_bits(num_udf);
 	core_writel(priv, reg, CORE_CFP_DATA_PORT(6));
 
@@ -626,11 +577,14 @@ static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port,
 	/* Mask all but valid UDFs */
 	core_writel(priv, udf_lower_bits(num_udf) << 24, CORE_CFP_MASK_PORT(5));
 
+	flow_rule_match_ipv6_addrs(flow_rule, &ipv6);
+	flow_rule_match_ports(flow_rule, &ports);
+
 	/* Slice the IPv6 source address and port */
-	bcm_sf2_cfp_slice_ipv6(priv, v6_spec->ip6src, v6_spec->psrc,
-				slice_num, false);
-	bcm_sf2_cfp_slice_ipv6(priv, v6_m_spec->ip6src, v6_m_spec->psrc,
-				SLICE_NUM_MASK, true);
+	bcm_sf2_cfp_slice_ipv6(priv, ipv6.key->src.in6_u.u6_addr32,
+			       ports.key->src, slice_num, false);
+	bcm_sf2_cfp_slice_ipv6(priv, ipv6.mask->src.in6_u.u6_addr32,
+			       ports.mask->src, SLICE_NUM_MASK, true);
 
 	/* Insert into TCAM now because we need to insert a second rule */
 	bcm_sf2_cfp_rule_addr_set(priv, rule_index[0]);
@@ -638,21 +592,19 @@ static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port,
 	ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | TCAM_SEL);
 	if (ret) {
 		pr_err("TCAM entry at addr %d failed\n", rule_index[0]);
-		goto out_err;
+		return ret;
 	}
 
 	/* Insert into Action and policer RAMs now */
 	ret = bcm_sf2_cfp_act_pol_set(priv, rule_index[0], port_num,
 				      queue_num, false);
 	if (ret)
-		goto out_err;
+		return ret;
 
 	/* Now deal with the second slice to chain this rule */
 	slice_num = bcm_sf2_get_slice_number(layout, slice_num + 1);
-	if (slice_num == UDF_NUM_SLICES) {
-		ret = -EINVAL;
-		goto out_err;
-	}
+	if (slice_num == UDF_NUM_SLICES)
+		return -EINVAL;
 
 	num_udf = bcm_sf2_get_num_udf_slices(layout->udfs[slice_num].slices);
 
@@ -687,10 +639,10 @@ static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port,
 	/* Mask all */
 	core_writel(priv, 0, CORE_CFP_MASK_PORT(5));
 
-	bcm_sf2_cfp_slice_ipv6(priv, v6_spec->ip6dst, v6_spec->pdst, slice_num,
-			       false);
-	bcm_sf2_cfp_slice_ipv6(priv, v6_m_spec->ip6dst, v6_m_spec->pdst,
-			       SLICE_NUM_MASK, true);
+	bcm_sf2_cfp_slice_ipv6(priv, ipv6.key->dst.in6_u.u6_addr32,
+			       ports.key->dst, slice_num, false);
+	bcm_sf2_cfp_slice_ipv6(priv, ipv6.mask->dst.in6_u.u6_addr32,
+			       ports.key->dst, SLICE_NUM_MASK, true);
 
 	/* Insert into TCAM now */
 	bcm_sf2_cfp_rule_addr_set(priv, rule_index[1]);
@@ -698,7 +650,7 @@ static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port,
 	ret = bcm_sf2_cfp_op(priv, OP_SEL_WRITE | TCAM_SEL);
 	if (ret) {
 		pr_err("TCAM entry at addr %d failed\n", rule_index[1]);
-		goto out_err;
+		return ret;
 	}
 
 	/* Insert into Action and policer RAMs now, set chain ID to
@@ -707,7 +659,7 @@ static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port,
 	ret = bcm_sf2_cfp_act_pol_set(priv, rule_index[1], port_num,
 				      queue_num, true);
 	if (ret)
-		goto out_err;
+		return ret;
 
 	/* Turn on CFP for this rule now */
 	reg = core_readl(priv, CORE_CFP_CTL_REG);
@@ -719,13 +671,8 @@ static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port,
 	 */
 	set_bit(rule_index[0], priv->cfp.used);
 	set_bit(rule_index[1], priv->cfp.unique);
-	fs->location = rule_index[1];
-
-	return ret;
 
-out_err:
-	clear_bit(rule_index[1], priv->cfp.used);
-	return ret;
+	return rule_index[1];
 }
 
 static int bcm_sf2_cfp_rule_set(struct dsa_switch *ds, int port,
@@ -735,7 +682,10 @@ static int bcm_sf2_cfp_rule_set(struct dsa_switch *ds, int port,
 	s8 cpu_port = ds->ports[port].cpu_dp->index;
 	__u64 ring_cookie = fs->ring_cookie;
 	unsigned int queue_num, port_num;
+	struct flow_rule *flow_rule;
+	unsigned int rule_index[2];
 	int ret = -EINVAL;
+	u8 ip_frag;
 
 	/* Check for unsupported extensions */
 	if ((fs->flow_type & FLOW_EXT) && (fs->m_ext.vlan_etype ||
@@ -750,23 +700,38 @@ static int bcm_sf2_cfp_rule_set(struct dsa_switch *ds, int port,
 	    fs->location > bcm_sf2_cfp_rule_size(priv))
 		return -EINVAL;
 
-	/* This rule is a Wake-on-LAN filter and we must specifically
-	 * target the CPU port in order for it to be working.
-	 */
-	if (ring_cookie == RX_CLS_FLOW_WAKE)
-		ring_cookie = cpu_port * SF2_NUM_EGRESS_QUEUES;
-
-	/* We do not support discarding packets, check that the
-	 * destination port is enabled and that we are within the
+	/* Check that the destination port is enabled and that we are within the
 	 * number of ports supported by the switch
 	 */
 	port_num = ring_cookie / SF2_NUM_EGRESS_QUEUES;
-
-	if (ring_cookie == RX_CLS_FLOW_DISC ||
-	    !(dsa_is_user_port(ds, port_num) ||
+	if (!(dsa_is_user_port(ds, port_num) ||
 	      dsa_is_cpu_port(ds, port_num)) ||
 	    port_num >= priv->hw_params.num_ports)
 		return -EINVAL;
+
+	/* User-defined data semantics depends on each driver. */
+	ip_frag = be32_to_cpu(fs->m_ext.data[0]);
+
+	flow_rule = ethtool_rx_flow_rule(fs);
+	if (!flow_rule)
+		return -ENOMEM;
+
+	switch (flow_rule->action.keys[0].id) {
+	case FLOW_ACTION_KEY_WAKE:
+		/* This rule is a Wake-on-LAN filter and we must specifically
+		 * target the CPU port in order for it to be working.
+		 */
+		ring_cookie = cpu_port * SF2_NUM_EGRESS_QUEUES;
+		break;
+	case FLOW_ACTION_KEY_QUEUE:
+		ring_cookie = flow_rule->action.keys[0].queue_index;
+		break;
+	default:
+		/* We do not support discarding packets. */
+		ret = -EINVAL;
+		goto err_out;
+	}
+
 	/*
 	 * We have a small oddity where Port 6 just does not have a
 	 * valid bit here (so we substract by one).
@@ -778,17 +743,73 @@ static int bcm_sf2_cfp_rule_set(struct dsa_switch *ds, int port,
 	switch (fs->flow_type & ~FLOW_EXT) {
 	case TCP_V4_FLOW:
 	case UDP_V4_FLOW:
+		/* Locate the first rule available */
+		if (fs->location == RX_CLS_LOC_ANY)
+			rule_index[0] = find_first_zero_bit(priv->cfp.used,
+							    priv->num_cfp_rules);
+		else
+			rule_index[0] = fs->location;
+
+		if (rule_index[0] > bcm_sf2_cfp_rule_size(priv)) {
+			ret = -ENOSPC;
+			goto err_out;
+		}
+
 		ret = bcm_sf2_cfp_ipv4_rule_set(priv, port, port_num,
-						queue_num, fs);
+						queue_num, flow_rule,
+						rule_index[0], ip_frag);
 		break;
 	case TCP_V6_FLOW:
 	case UDP_V6_FLOW:
+		/* Negotiate two indexes, one for the second half which we are
+		 * chained from, which is what we will return to user-space, and
+		 * a second one which is used to store its first half. That
+		 * first half does not allow any choice of placement, so it just
+		 * needs to find the next available bit. We return the second
+		 * half as fs->location because that helps with the rule lookup
+		 * later on since the second half is chained from its first
+		 * half, we can easily identify IPv6 CFP rules by looking
+		 * whether they carry a CHAIN_ID.
+		 *
+		 * We also want the second half to have a lower rule_index than
+		 * its first half because the HW search is by incrementing
+		 * addresses.
+		 */
+		if (fs->location == RX_CLS_LOC_ANY)
+			rule_index[1] = find_first_zero_bit(priv->cfp.used,
+							    priv->num_cfp_rules);
+		else
+			rule_index[1] = fs->location;
+
+		if (rule_index[1] > bcm_sf2_cfp_rule_size(priv)) {
+			ret = -ENOSPC;
+			goto err_out;
+		}
+
+		/* Flag it as used (cleared on error path) such that we can
+		 * immediately obtain a second one to chain from.
+		 */
+		set_bit(rule_index[1], priv->cfp.used);
+
+		rule_index[0] = find_first_zero_bit(priv->cfp.used,
+						    priv->num_cfp_rules);
+		if (rule_index[0] > bcm_sf2_cfp_rule_size(priv)) {
+			clear_bit(rule_index[1], priv->cfp.used);
+			ret = -ENOSPC;
+			goto err_out;
+		}
+
 		ret = bcm_sf2_cfp_ipv6_rule_set(priv, port, port_num,
-						queue_num, fs);
+						queue_num, flow_rule,
+						rule_index, ip_frag);
 		break;
 	default:
 		break;
 	}
+	if (ret >= 0)
+		fs->location = ret;
+err_out:
+	ethtool_rx_flow_rule_free(flow_rule);
 
 	return ret;
 }
-- 
2.11.0