Commit 8a75f4f2 authored by Florian Fainelli's avatar Florian Fainelli Committed by David S. Miller

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: default avatarFlorian Fainelli <f.fainelli@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 6cfef793
...@@ -732,6 +732,8 @@ static int bcm_sf2_cfp_rule_set(struct dsa_switch *ds, int port, ...@@ -732,6 +732,8 @@ static int bcm_sf2_cfp_rule_set(struct dsa_switch *ds, int port,
struct ethtool_rx_flow_spec *fs) struct ethtool_rx_flow_spec *fs)
{ {
struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); 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; unsigned int queue_num, port_num;
int ret = -EINVAL; int ret = -EINVAL;
...@@ -748,13 +750,19 @@ static int bcm_sf2_cfp_rule_set(struct dsa_switch *ds, int port, ...@@ -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)) fs->location > bcm_sf2_cfp_rule_size(priv))
return -EINVAL; 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 /* We do not support discarding packets, check that the
* destination port is enabled and that we are within the * destination port is enabled and that we are within the
* number of ports supported by the switch * 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_user_port(ds, port_num) ||
dsa_is_cpu_port(ds, port_num)) || dsa_is_cpu_port(ds, port_num)) ||
port_num >= priv->hw_params.num_ports) port_num >= priv->hw_params.num_ports)
...@@ -763,7 +771,7 @@ static int bcm_sf2_cfp_rule_set(struct dsa_switch *ds, int port, ...@@ -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 * We have a small oddity where Port 6 just does not have a
* valid bit here (so we substract by one). * 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) if (port_num >= 7)
port_num -= 1; port_num -= 1;
...@@ -1188,6 +1196,7 @@ static int bcm_sf2_cfp_rule_get_all(struct bcm_sf2_priv *priv, ...@@ -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, int bcm_sf2_get_rxnfc(struct dsa_switch *ds, int port,
struct ethtool_rxnfc *nfc, u32 *rule_locs) 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); struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
int ret = 0; int ret = 0;
...@@ -1214,12 +1223,23 @@ int bcm_sf2_get_rxnfc(struct dsa_switch *ds, int port, ...@@ -1214,12 +1223,23 @@ int bcm_sf2_get_rxnfc(struct dsa_switch *ds, int port,
mutex_unlock(&priv->cfp.lock); 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; return ret;
} }
int bcm_sf2_set_rxnfc(struct dsa_switch *ds, int port, int bcm_sf2_set_rxnfc(struct dsa_switch *ds, int port,
struct ethtool_rxnfc *nfc) struct ethtool_rxnfc *nfc)
{ {
struct net_device *p = ds->ports[port].cpu_dp->master;
struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
int ret = 0; int ret = 0;
...@@ -1240,6 +1260,23 @@ int bcm_sf2_set_rxnfc(struct dsa_switch *ds, int port, ...@@ -1240,6 +1260,23 @@ int bcm_sf2_set_rxnfc(struct dsa_switch *ds, int port,
mutex_unlock(&priv->cfp.lock); 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; return ret;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment