diff options
Diffstat (limited to 'drivers/net/ethernet/mscc/ocelot_net.c')
-rw-r--r-- | drivers/net/ethernet/mscc/ocelot_net.c | 251 |
1 files changed, 196 insertions, 55 deletions
diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c index e271b6225b72..247bc105bdd2 100644 --- a/drivers/net/ethernet/mscc/ocelot_net.c +++ b/drivers/net/ethernet/mscc/ocelot_net.c @@ -14,11 +14,14 @@ #include <linux/phy/phy.h> #include <net/pkt_cls.h> #include "ocelot.h" +#include "ocelot_police.h" #include "ocelot_vcap.h" #include "ocelot_fdma.h" #define OCELOT_MAC_QUIRKS OCELOT_QUIRK_QSGMII_PORTS_MUST_BE_UP +static bool ocelot_netdevice_dev_check(const struct net_device *dev); + static struct ocelot *devlink_port_to_ocelot(struct devlink_port *dlp) { return devlink_priv(dlp->devlink); @@ -215,14 +218,14 @@ int ocelot_setup_tc_cls_flower(struct ocelot_port_private *priv, } } -static int ocelot_setup_tc_cls_matchall(struct ocelot_port_private *priv, - struct tc_cls_matchall_offload *f, - bool ingress) +static int ocelot_setup_tc_cls_matchall_police(struct ocelot_port_private *priv, + struct tc_cls_matchall_offload *f, + bool ingress, + struct netlink_ext_ack *extack) { - struct netlink_ext_ack *extack = f->common.extack; + struct flow_action_entry *action = &f->rule->action.entries[0]; struct ocelot *ocelot = priv->port.ocelot; struct ocelot_policer pol = { 0 }; - struct flow_action_entry *action; int port = priv->chip_port; int err; @@ -231,6 +234,119 @@ static int ocelot_setup_tc_cls_matchall(struct ocelot_port_private *priv, return -EOPNOTSUPP; } + if (priv->tc.police_id && priv->tc.police_id != f->cookie) { + NL_SET_ERR_MSG_MOD(extack, + "Only one policer per port is supported"); + return -EEXIST; + } + + err = ocelot_policer_validate(&f->rule->action, action, extack); + if (err) + return err; + + pol.rate = (u32)div_u64(action->police.rate_bytes_ps, 1000) * 8; + pol.burst = action->police.burst; + + err = ocelot_port_policer_add(ocelot, port, &pol); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Could not add policer"); + return err; + } + + priv->tc.police_id = f->cookie; + priv->tc.offload_cnt++; + + return 0; +} + +static int ocelot_setup_tc_cls_matchall_mirred(struct ocelot_port_private *priv, + struct tc_cls_matchall_offload *f, + bool ingress, + struct netlink_ext_ack *extack) +{ + struct flow_action *action = &f->rule->action; + struct ocelot *ocelot = priv->port.ocelot; + struct ocelot_port_private *other_priv; + const struct flow_action_entry *a; + int err; + + if (f->common.protocol != htons(ETH_P_ALL)) + return -EOPNOTSUPP; + + if (!flow_action_basic_hw_stats_check(action, extack)) + return -EOPNOTSUPP; + + a = &action->entries[0]; + if (!a->dev) + return -EINVAL; + + if (!ocelot_netdevice_dev_check(a->dev)) { + NL_SET_ERR_MSG_MOD(extack, + "Destination not an ocelot port"); + return -EOPNOTSUPP; + } + + other_priv = netdev_priv(a->dev); + + err = ocelot_port_mirror_add(ocelot, priv->chip_port, + other_priv->chip_port, ingress, extack); + if (err) + return err; + + if (ingress) + priv->tc.ingress_mirred_id = f->cookie; + else + priv->tc.egress_mirred_id = f->cookie; + priv->tc.offload_cnt++; + + return 0; +} + +static int ocelot_del_tc_cls_matchall_police(struct ocelot_port_private *priv, + struct netlink_ext_ack *extack) +{ + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; + int err; + + err = ocelot_port_policer_del(ocelot, port); + if (err) { + NL_SET_ERR_MSG_MOD(extack, + "Could not delete policer"); + return err; + } + + priv->tc.police_id = 0; + priv->tc.offload_cnt--; + + return 0; +} + +static int ocelot_del_tc_cls_matchall_mirred(struct ocelot_port_private *priv, + bool ingress, + struct netlink_ext_ack *extack) +{ + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; + + ocelot_port_mirror_del(ocelot, port, ingress); + + if (ingress) + priv->tc.ingress_mirred_id = 0; + else + priv->tc.egress_mirred_id = 0; + priv->tc.offload_cnt--; + + return 0; +} + +static int ocelot_setup_tc_cls_matchall(struct ocelot_port_private *priv, + struct tc_cls_matchall_offload *f, + bool ingress) +{ + struct netlink_ext_ack *extack = f->common.extack; + struct flow_action_entry *action; + switch (f->command) { case TC_CLSMATCHALL_REPLACE: if (!flow_offload_has_one_action(&f->rule->action)) { @@ -241,54 +357,41 @@ static int ocelot_setup_tc_cls_matchall(struct ocelot_port_private *priv, if (priv->tc.block_shared) { NL_SET_ERR_MSG_MOD(extack, - "Rate limit is not supported on shared blocks"); + "Matchall offloads not supported on shared blocks"); return -EOPNOTSUPP; } action = &f->rule->action.entries[0]; - if (action->id != FLOW_ACTION_POLICE) { + switch (action->id) { + case FLOW_ACTION_POLICE: + return ocelot_setup_tc_cls_matchall_police(priv, f, + ingress, + extack); + break; + case FLOW_ACTION_MIRRED: + return ocelot_setup_tc_cls_matchall_mirred(priv, f, + ingress, + extack); + default: NL_SET_ERR_MSG_MOD(extack, "Unsupported action"); return -EOPNOTSUPP; } - if (priv->tc.police_id && priv->tc.police_id != f->cookie) { - NL_SET_ERR_MSG_MOD(extack, - "Only one policer per port is supported"); - 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; - - err = ocelot_port_policer_add(ocelot, port, &pol); - if (err) { - NL_SET_ERR_MSG_MOD(extack, "Could not add policer"); - return err; - } - - priv->tc.police_id = f->cookie; - priv->tc.offload_cnt++; - return 0; + break; case TC_CLSMATCHALL_DESTROY: - if (priv->tc.police_id != f->cookie) + action = &f->rule->action.entries[0]; + + if (f->cookie == priv->tc.police_id) + return ocelot_del_tc_cls_matchall_police(priv, extack); + else if (f->cookie == priv->tc.ingress_mirred_id || + f->cookie == priv->tc.egress_mirred_id) + return ocelot_del_tc_cls_matchall_mirred(priv, ingress, + extack); + else return -ENOENT; - err = ocelot_port_policer_del(ocelot, port); - if (err) { - NL_SET_ERR_MSG_MOD(extack, - "Could not delete policer"); - return err; - } - priv->tc.police_id = 0; - priv->tc.offload_cnt--; - return 0; + break; case TC_CLSMATCHALL_STATS: default: return -EOPNOTSUPP; @@ -419,7 +522,7 @@ static int ocelot_vlan_vid_del(struct net_device *dev, u16 vid) * with VLAN filtering feature. We need to keep it to receive * untagged traffic. */ - if (vid == OCELOT_VLAN_UNAWARE_PVID) + if (vid == OCELOT_STANDALONE_PVID) return 0; ret = ocelot_vlan_del(ocelot, port, vid); @@ -559,7 +662,7 @@ static int ocelot_mc_unsync(struct net_device *dev, const unsigned char *addr) struct ocelot_mact_work_ctx w; ether_addr_copy(w.forget.addr, addr); - w.forget.vid = OCELOT_VLAN_UNAWARE_PVID; + w.forget.vid = OCELOT_STANDALONE_PVID; w.type = OCELOT_MACT_FORGET; return ocelot_enqueue_mact_action(ocelot, &w); @@ -573,7 +676,7 @@ static int ocelot_mc_sync(struct net_device *dev, const unsigned char *addr) struct ocelot_mact_work_ctx w; ether_addr_copy(w.learn.addr, addr); - w.learn.vid = OCELOT_VLAN_UNAWARE_PVID; + w.learn.vid = OCELOT_STANDALONE_PVID; w.learn.pgid = PGID_CPU; w.learn.entry_type = ENTRYTYPE_LOCKED; w.type = OCELOT_MACT_LEARN; @@ -608,9 +711,9 @@ static int ocelot_port_set_mac_address(struct net_device *dev, void *p) /* Learn the new net device MAC address in the mac table. */ ocelot_mact_learn(ocelot, PGID_CPU, addr->sa_data, - OCELOT_VLAN_UNAWARE_PVID, ENTRYTYPE_LOCKED); + OCELOT_STANDALONE_PVID, ENTRYTYPE_LOCKED); /* Then forget the previous one. */ - ocelot_mact_forget(ocelot, dev->dev_addr, OCELOT_VLAN_UNAWARE_PVID); + ocelot_mact_forget(ocelot, dev->dev_addr, OCELOT_STANDALONE_PVID); eth_hw_addr_set(dev, addr->sa_data); return 0; @@ -662,10 +765,11 @@ static int ocelot_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], struct netlink_ext_ack *extack) { struct ocelot_port_private *priv = netdev_priv(dev); - struct ocelot *ocelot = priv->port.ocelot; + struct ocelot_port *ocelot_port = &priv->port; + struct ocelot *ocelot = ocelot_port->ocelot; int port = priv->chip_port; - return ocelot_fdb_add(ocelot, port, addr, vid); + return ocelot_fdb_add(ocelot, port, addr, vid, ocelot_port->bridge); } static int ocelot_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], @@ -673,10 +777,11 @@ static int ocelot_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], const unsigned char *addr, u16 vid) { struct ocelot_port_private *priv = netdev_priv(dev); - struct ocelot *ocelot = priv->port.ocelot; + struct ocelot_port *ocelot_port = &priv->port; + struct ocelot *ocelot = ocelot_port->ocelot; int port = priv->chip_port; - return ocelot_fdb_del(ocelot, port, addr, vid); + return ocelot_fdb_del(ocelot, port, addr, vid, ocelot_port->bridge); } static int ocelot_port_fdb_dump(struct sk_buff *skb, @@ -988,7 +1093,7 @@ static int ocelot_port_obj_add_mdb(struct net_device *dev, struct ocelot *ocelot = ocelot_port->ocelot; int port = priv->chip_port; - return ocelot_port_mdb_add(ocelot, port, mdb); + return ocelot_port_mdb_add(ocelot, port, mdb, ocelot_port->bridge); } static int ocelot_port_obj_del_mdb(struct net_device *dev, @@ -999,7 +1104,7 @@ static int ocelot_port_obj_del_mdb(struct net_device *dev, struct ocelot *ocelot = ocelot_port->ocelot; int port = priv->chip_port; - return ocelot_port_mdb_del(ocelot, port, mdb); + return ocelot_port_mdb_del(ocelot, port, mdb, ocelot_port->bridge); } static int ocelot_port_obj_mrp_add(struct net_device *dev, @@ -1173,6 +1278,33 @@ static int ocelot_switchdev_unsync(struct ocelot *ocelot, int port) return 0; } +static int ocelot_bridge_num_get(struct ocelot *ocelot, + const struct net_device *bridge_dev) +{ + int bridge_num = ocelot_bridge_num_find(ocelot, bridge_dev); + + if (bridge_num < 0) { + /* First port that offloads this bridge */ + bridge_num = find_first_zero_bit(&ocelot->bridges, + ocelot->num_phys_ports); + + set_bit(bridge_num, &ocelot->bridges); + } + + return bridge_num; +} + +static void ocelot_bridge_num_put(struct ocelot *ocelot, + const struct net_device *bridge_dev, + int bridge_num) +{ + /* Check if the bridge is still in use, otherwise it is time + * to clean it up so we can reuse this bridge_num later. + */ + if (!ocelot_bridge_num_find(ocelot, bridge_dev)) + clear_bit(bridge_num, &ocelot->bridges); +} + static int ocelot_netdevice_bridge_join(struct net_device *dev, struct net_device *brport_dev, struct net_device *bridge, @@ -1182,9 +1314,14 @@ static int ocelot_netdevice_bridge_join(struct net_device *dev, struct ocelot_port *ocelot_port = &priv->port; struct ocelot *ocelot = ocelot_port->ocelot; int port = priv->chip_port; - int err; + int bridge_num, err; + + bridge_num = ocelot_bridge_num_get(ocelot, bridge); - ocelot_port_bridge_join(ocelot, port, bridge); + err = ocelot_port_bridge_join(ocelot, port, bridge, bridge_num, + extack); + if (err) + goto err_join; err = switchdev_bridge_port_offload(brport_dev, dev, priv, &ocelot_switchdev_nb, @@ -1205,6 +1342,8 @@ err_switchdev_sync: &ocelot_switchdev_blocking_nb); err_switchdev_offload: ocelot_port_bridge_leave(ocelot, port, bridge); +err_join: + ocelot_bridge_num_put(ocelot, bridge, bridge_num); return err; } @@ -1225,6 +1364,7 @@ static int ocelot_netdevice_bridge_leave(struct net_device *dev, struct ocelot_port_private *priv = netdev_priv(dev); struct ocelot_port *ocelot_port = &priv->port; struct ocelot *ocelot = ocelot_port->ocelot; + int bridge_num = ocelot_port->bridge_num; int port = priv->chip_port; int err; @@ -1233,6 +1373,7 @@ static int ocelot_netdevice_bridge_leave(struct net_device *dev, return err; ocelot_port_bridge_leave(ocelot, port, bridge); + ocelot_bridge_num_put(ocelot, bridge, bridge_num); return 0; } @@ -1700,7 +1841,7 @@ int ocelot_probe_port(struct ocelot *ocelot, int port, struct regmap *target, eth_hw_addr_gen(dev, ocelot->base_mac, port); ocelot_mact_learn(ocelot, PGID_CPU, dev->dev_addr, - OCELOT_VLAN_UNAWARE_PVID, ENTRYTYPE_LOCKED); + OCELOT_STANDALONE_PVID, ENTRYTYPE_LOCKED); ocelot_init_port(ocelot, port); |