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 <pa...@netfilter.org> --- 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