diff options
author | Florian Fainelli <f.fainelli@gmail.com> | 2018-08-07 20:50:22 +0300 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-08-07 22:15:03 +0300 |
commit | 8a75f4f2acd78adfaf4b96939322d8a1de97d378 (patch) | |
tree | 4f9af091cedf7770d5533a6638901323861d01ac /drivers | |
parent | 6cfef793b558eee47bac720574aff0d36b89d20a (diff) | |
download | linux-8a75f4f2acd78adfaf4b96939322d8a1de97d378.tar.xz |
net: dsa: bcm_sf2: Propagate ethtool::rxnfc to CPU port
Allow propagating ethtool::rxnfc programming to the CPU/management port
such that it is possible for such a CPU to perform e.g: Wake-on-LAN
using filters configured by the switch. We need a tiny bit of
cooperation between the switch drivers which is able to do the full flow
matching, whereas the CPU/management port might not. The CPU/management
driver needs to return -EOPNOTSUPP to indicate an non critical error,
any other error code otherwise.
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/dsa/bcm_sf2_cfp.c | 43 |
1 files changed, 40 insertions, 3 deletions
diff --git a/drivers/net/dsa/bcm_sf2_cfp.c b/drivers/net/dsa/bcm_sf2_cfp.c index 1e37b65aab93..47c5f272a084 100644 --- a/drivers/net/dsa/bcm_sf2_cfp.c +++ b/drivers/net/dsa/bcm_sf2_cfp.c @@ -732,6 +732,8 @@ static int bcm_sf2_cfp_rule_set(struct dsa_switch *ds, int port, struct ethtool_rx_flow_spec *fs) { struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); + s8 cpu_port = ds->ports[port].cpu_dp->index; + __u64 ring_cookie = fs->ring_cookie; unsigned int queue_num, port_num; int ret = -EINVAL; @@ -748,13 +750,19 @@ 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 * number of ports supported by the switch */ - port_num = fs->ring_cookie / SF2_NUM_EGRESS_QUEUES; + port_num = ring_cookie / SF2_NUM_EGRESS_QUEUES; - if (fs->ring_cookie == RX_CLS_FLOW_DISC || + if (ring_cookie == RX_CLS_FLOW_DISC || !(dsa_is_user_port(ds, port_num) || dsa_is_cpu_port(ds, port_num)) || port_num >= priv->hw_params.num_ports) @@ -763,7 +771,7 @@ static int bcm_sf2_cfp_rule_set(struct dsa_switch *ds, int port, * We have a small oddity where Port 6 just does not have a * valid bit here (so we substract by one). */ - queue_num = fs->ring_cookie % SF2_NUM_EGRESS_QUEUES; + queue_num = ring_cookie % SF2_NUM_EGRESS_QUEUES; if (port_num >= 7) port_num -= 1; @@ -1188,6 +1196,7 @@ static int bcm_sf2_cfp_rule_get_all(struct bcm_sf2_priv *priv, int bcm_sf2_get_rxnfc(struct dsa_switch *ds, int port, struct ethtool_rxnfc *nfc, u32 *rule_locs) { + struct net_device *p = ds->ports[port].cpu_dp->master; struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); int ret = 0; @@ -1214,12 +1223,23 @@ int bcm_sf2_get_rxnfc(struct dsa_switch *ds, int port, mutex_unlock(&priv->cfp.lock); + if (ret) + return ret; + + /* Pass up the commands to the attached master network device */ + if (p->ethtool_ops->get_rxnfc) { + ret = p->ethtool_ops->get_rxnfc(p, nfc, rule_locs); + if (ret == -EOPNOTSUPP) + ret = 0; + } + return ret; } int bcm_sf2_set_rxnfc(struct dsa_switch *ds, int port, struct ethtool_rxnfc *nfc) { + struct net_device *p = ds->ports[port].cpu_dp->master; struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); int ret = 0; @@ -1240,6 +1260,23 @@ int bcm_sf2_set_rxnfc(struct dsa_switch *ds, int port, mutex_unlock(&priv->cfp.lock); + if (ret) + return ret; + + /* Pass up the commands to the attached master network device. + * This can fail, so rollback the operation if we need to. + */ + if (p->ethtool_ops->set_rxnfc) { + ret = p->ethtool_ops->set_rxnfc(p, nfc); + if (ret && ret != -EOPNOTSUPP) { + mutex_lock(&priv->cfp.lock); + bcm_sf2_cfp_rule_del(priv, port, nfc->fs.location); + mutex_unlock(&priv->cfp.lock); + } else { + ret = 0; + } + } + return ret; } |