summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/mscc/ocelot_net.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ethernet/mscc/ocelot_net.c')
-rw-r--r--drivers/net/ethernet/mscc/ocelot_net.c234
1 files changed, 189 insertions, 45 deletions
diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c
index 12cb6867a2d0..aad33d22c33f 100644
--- a/drivers/net/ethernet/mscc/ocelot_net.c
+++ b/drivers/net/ethernet/mscc/ocelot_net.c
@@ -251,6 +251,12 @@ static int ocelot_setup_tc_cls_matchall(struct ocelot_port_private *priv,
return -EEXIST;
}
+ if (action->police.rate_pkt_ps) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "QoS offload not support packets per second");
+ return -EOPNOTSUPP;
+ }
+
pol.rate = (u32)div_u64(action->police.rate_bytes_ps, 1000) * 8;
pol.burst = action->police.burst;
@@ -501,21 +507,17 @@ static netdev_tx_t ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
/* Check if timestamping is needed */
if (ocelot->ptp && (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
- rew_op = ocelot_port->ptp_cmd;
+ struct sk_buff *clone = NULL;
- if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
- struct sk_buff *clone;
-
- clone = skb_clone_sk(skb);
- if (!clone) {
- kfree_skb(skb);
- return NETDEV_TX_OK;
- }
+ if (ocelot_port_txtstamp_request(ocelot, port, skb, &clone)) {
+ kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
- ocelot_port_add_txtstamp_skb(ocelot, port, clone);
+ if (clone)
+ OCELOT_SKB_CB(skb)->clone = clone;
- rew_op |= clone->cb[0] << 3;
- }
+ rew_op = ocelot_ptp_rew_op(skb);
}
ocelot_port_inject_frame(ocelot, port, 0, rew_op, skb);
@@ -1111,77 +1113,213 @@ static int ocelot_port_obj_del(struct net_device *dev,
return ret;
}
-static int ocelot_netdevice_bridge_join(struct ocelot *ocelot, int port,
- struct net_device *bridge)
+static void ocelot_inherit_brport_flags(struct ocelot *ocelot, int port,
+ struct net_device *brport_dev)
+{
+ struct switchdev_brport_flags flags = {0};
+ int flag;
+
+ flags.mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD;
+
+ for_each_set_bit(flag, &flags.mask, 32)
+ if (br_port_flag_is_set(brport_dev, BIT(flag)))
+ flags.val |= BIT(flag);
+
+ ocelot_port_bridge_flags(ocelot, port, flags);
+}
+
+static void ocelot_clear_brport_flags(struct ocelot *ocelot, int port)
{
struct switchdev_brport_flags flags;
- int err;
flags.mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD;
- flags.val = flags.mask;
+ flags.val = flags.mask & ~BR_LEARNING;
+
+ ocelot_port_bridge_flags(ocelot, port, flags);
+}
- err = ocelot_port_bridge_join(ocelot, port, bridge);
+static int ocelot_switchdev_sync(struct ocelot *ocelot, int port,
+ struct net_device *brport_dev,
+ struct net_device *bridge_dev,
+ struct netlink_ext_ack *extack)
+{
+ clock_t ageing_time;
+ u8 stp_state;
+ int err;
+
+ ocelot_inherit_brport_flags(ocelot, port, brport_dev);
+
+ stp_state = br_port_get_stp_state(brport_dev);
+ ocelot_bridge_stp_state_set(ocelot, port, stp_state);
+
+ err = ocelot_port_vlan_filtering(ocelot, port,
+ br_vlan_enabled(bridge_dev));
if (err)
return err;
- ocelot_port_bridge_flags(ocelot, port, flags);
+ ageing_time = br_get_ageing_time(bridge_dev);
+ ocelot_port_attr_ageing_set(ocelot, port, ageing_time);
+
+ err = br_mdb_replay(bridge_dev, brport_dev,
+ &ocelot_switchdev_blocking_nb, extack);
+ if (err && err != -EOPNOTSUPP)
+ return err;
+
+ err = br_fdb_replay(bridge_dev, brport_dev, &ocelot_switchdev_nb);
+ if (err)
+ return err;
+
+ err = br_vlan_replay(bridge_dev, brport_dev,
+ &ocelot_switchdev_blocking_nb, extack);
+ if (err && err != -EOPNOTSUPP)
+ return err;
+
+ return 0;
+}
+
+static int ocelot_switchdev_unsync(struct ocelot *ocelot, int port)
+{
+ int err;
+
+ err = ocelot_port_vlan_filtering(ocelot, port, false);
+ if (err)
+ return err;
+
+ ocelot_clear_brport_flags(ocelot, port);
+
+ ocelot_bridge_stp_state_set(ocelot, port, BR_STATE_FORWARDING);
+
+ return 0;
+}
+
+static int ocelot_netdevice_bridge_join(struct net_device *dev,
+ struct net_device *brport_dev,
+ struct net_device *bridge,
+ struct netlink_ext_ack *extack)
+{
+ struct ocelot_port_private *priv = netdev_priv(dev);
+ struct ocelot_port *ocelot_port = &priv->port;
+ struct ocelot *ocelot = ocelot_port->ocelot;
+ int port = priv->chip_port;
+ int err;
+
+ ocelot_port_bridge_join(ocelot, port, bridge);
+
+ err = ocelot_switchdev_sync(ocelot, port, brport_dev, bridge, extack);
+ if (err)
+ goto err_switchdev_sync;
return 0;
+
+err_switchdev_sync:
+ ocelot_port_bridge_leave(ocelot, port, bridge);
+ return err;
}
-static int ocelot_netdevice_bridge_leave(struct ocelot *ocelot, int port,
+static int ocelot_netdevice_bridge_leave(struct net_device *dev,
+ struct net_device *brport_dev,
struct net_device *bridge)
{
- struct switchdev_brport_flags flags;
+ struct ocelot_port_private *priv = netdev_priv(dev);
+ struct ocelot_port *ocelot_port = &priv->port;
+ struct ocelot *ocelot = ocelot_port->ocelot;
+ int port = priv->chip_port;
int err;
- flags.mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD;
- flags.val = flags.mask & ~BR_LEARNING;
+ err = ocelot_switchdev_unsync(ocelot, port);
+ if (err)
+ return err;
- err = ocelot_port_bridge_leave(ocelot, port, bridge);
+ ocelot_port_bridge_leave(ocelot, port, bridge);
- ocelot_port_bridge_flags(ocelot, port, flags);
+ return 0;
+}
+
+static int ocelot_netdevice_lag_join(struct net_device *dev,
+ struct net_device *bond,
+ struct netdev_lag_upper_info *info,
+ struct netlink_ext_ack *extack)
+{
+ struct ocelot_port_private *priv = netdev_priv(dev);
+ struct ocelot_port *ocelot_port = &priv->port;
+ struct ocelot *ocelot = ocelot_port->ocelot;
+ struct net_device *bridge_dev;
+ int port = priv->chip_port;
+ int err;
+ err = ocelot_port_lag_join(ocelot, port, bond, info);
+ if (err == -EOPNOTSUPP) {
+ NL_SET_ERR_MSG_MOD(extack, "Offloading not supported");
+ return 0;
+ }
+
+ bridge_dev = netdev_master_upper_dev_get(bond);
+ if (!bridge_dev || !netif_is_bridge_master(bridge_dev))
+ return 0;
+
+ err = ocelot_netdevice_bridge_join(dev, bond, bridge_dev, extack);
+ if (err)
+ goto err_bridge_join;
+
+ return 0;
+
+err_bridge_join:
+ ocelot_port_lag_leave(ocelot, port, bond);
return err;
}
-static int ocelot_netdevice_changeupper(struct net_device *dev,
- struct netdev_notifier_changeupper_info *info)
+static int ocelot_netdevice_lag_leave(struct net_device *dev,
+ struct net_device *bond)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot;
+ struct net_device *bridge_dev;
int port = priv->chip_port;
+
+ ocelot_port_lag_leave(ocelot, port, bond);
+
+ bridge_dev = netdev_master_upper_dev_get(bond);
+ if (!bridge_dev || !netif_is_bridge_master(bridge_dev))
+ return 0;
+
+ return ocelot_netdevice_bridge_leave(dev, bond, bridge_dev);
+}
+
+static int ocelot_netdevice_changeupper(struct net_device *dev,
+ struct netdev_notifier_changeupper_info *info)
+{
+ struct netlink_ext_ack *extack;
int err = 0;
+ extack = netdev_notifier_info_to_extack(&info->info);
+
if (netif_is_bridge_master(info->upper_dev)) {
- if (info->linking) {
- err = ocelot_netdevice_bridge_join(ocelot, port,
- info->upper_dev);
- } else {
- err = ocelot_netdevice_bridge_leave(ocelot, port,
+ if (info->linking)
+ err = ocelot_netdevice_bridge_join(dev, dev,
+ info->upper_dev,
+ extack);
+ else
+ err = ocelot_netdevice_bridge_leave(dev, dev,
info->upper_dev);
- }
}
if (netif_is_lag_master(info->upper_dev)) {
- if (info->linking) {
- err = ocelot_port_lag_join(ocelot, port,
- info->upper_dev,
- info->upper_info);
- if (err == -EOPNOTSUPP) {
- NL_SET_ERR_MSG_MOD(info->info.extack,
- "Offloading not supported");
- err = 0;
- }
- } else {
- ocelot_port_lag_leave(ocelot, port,
- info->upper_dev);
- }
+ if (info->linking)
+ err = ocelot_netdevice_lag_join(dev, info->upper_dev,
+ info->upper_info, extack);
+ else
+ ocelot_netdevice_lag_leave(dev, info->upper_dev);
}
return notifier_from_errno(err);
}
+/* Treat CHANGEUPPER events on an offloaded LAG as individual CHANGEUPPER
+ * events for the lower physical ports of the LAG.
+ * If the LAG upper isn't offloaded, ignore its CHANGEUPPER events.
+ * In case the LAG joined a bridge, notify that we are offloading it and can do
+ * forwarding in hardware towards it.
+ */
static int
ocelot_netdevice_lag_changeupper(struct net_device *dev,
struct netdev_notifier_changeupper_info *info)
@@ -1191,6 +1329,12 @@ ocelot_netdevice_lag_changeupper(struct net_device *dev,
int err = NOTIFY_DONE;
netdev_for_each_lower_dev(dev, lower, iter) {
+ struct ocelot_port_private *priv = netdev_priv(lower);
+ struct ocelot_port *ocelot_port = &priv->port;
+
+ if (ocelot_port->bond != dev)
+ return NOTIFY_OK;
+
err = ocelot_netdevice_changeupper(lower, info);
if (err)
return notifier_from_errno(err);