[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20180925192001.2482-11-pablo@netfilter.org>
Date: Tue, 25 Sep 2018 21:20:01 +0200
From: Pablo Neira Ayuso <pablo@...filter.org>
To: netdev@...r.kernel.org
Cc: davem@...emloft.net, thomas.lendacky@....com, f.fainelli@...il.com,
ariel.elior@...ium.com, michael.chan@...adcom.com,
santosh@...lsio.com, madalin.bucur@....com,
yisen.zhuang@...wei.com, salil.mehta@...wei.com,
jeffrey.t.kirsher@...el.com, tariqt@...lanox.com,
saeedm@...lanox.com, jiri@...lanox.com, idosch@...lanox.com,
ganeshgr@...lsio.com, jakub.kicinski@...ronome.com,
linux-net-drivers@...arflare.com, peppe.cavallaro@...com,
alexandre.torgue@...com, joabreu@...opsys.com,
grygorii.strashko@...com, andrew@...n.ch,
vivien.didelot@...oirfairelinux.com
Subject: [PATCH RFC,net-next 10/10] dsa: bcm_sf2: use flow_rule infrastructure
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 <pablo@...filter.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
Powered by blists - more mailing lists