summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/ethernet/mscc/ocelot.c12
-rw-r--r--drivers/net/ethernet/mscc/ocelot_mrp.c233
-rw-r--r--include/linux/dsa/ocelot.h5
-rw-r--r--include/soc/mscc/ocelot.h12
-rw-r--r--net/dsa/tag_ocelot.c8
5 files changed, 167 insertions, 103 deletions
diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
index 46e5c9136bac..9f0c9bdd9f5d 100644
--- a/drivers/net/ethernet/mscc/ocelot.c
+++ b/drivers/net/ethernet/mscc/ocelot.c
@@ -687,7 +687,7 @@ static int ocelot_xtr_poll_xfh(struct ocelot *ocelot, int grp, u32 *xfh)
int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp, struct sk_buff **nskb)
{
struct skb_shared_hwtstamps *shhwtstamps;
- u64 tod_in_ns, full_ts_in_ns, cpuq;
+ u64 tod_in_ns, full_ts_in_ns;
u64 timestamp, src_port, len;
u32 xfh[OCELOT_TAG_LEN / 4];
struct net_device *dev;
@@ -704,7 +704,6 @@ int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp, struct sk_buff **nskb)
ocelot_xfh_get_src_port(xfh, &src_port);
ocelot_xfh_get_len(xfh, &len);
ocelot_xfh_get_rew_val(xfh, &timestamp);
- ocelot_xfh_get_cpuq(xfh, &cpuq);
if (WARN_ON(src_port >= ocelot->num_phys_ports))
return -EINVAL;
@@ -772,12 +771,6 @@ int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp, struct sk_buff **nskb)
skb->protocol = eth_type_trans(skb, dev);
-#if IS_ENABLED(CONFIG_BRIDGE_MRP)
- if (skb->protocol == cpu_to_be16(ETH_P_MRP) &&
- cpuq & BIT(OCELOT_MRP_CPUQ))
- skb->offload_fwd_mark = 0;
-#endif
-
*nskb = skb;
return 0;
@@ -2051,6 +2044,9 @@ int ocelot_init(struct ocelot *ocelot)
ocelot_write_rix(ocelot, val, ANA_PGID_PGID, i);
}
+
+ ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, PGID_BLACKHOLE);
+
/* Allow broadcast and unknown L2 multicast to the CPU. */
ocelot_rmw_rix(ocelot, ANA_PGID_PGID_PGID(BIT(ocelot->num_phys_ports)),
ANA_PGID_PGID_PGID(BIT(ocelot->num_phys_ports)),
diff --git a/drivers/net/ethernet/mscc/ocelot_mrp.c b/drivers/net/ethernet/mscc/ocelot_mrp.c
index 683da320bfd8..439129a65b71 100644
--- a/drivers/net/ethernet/mscc/ocelot_mrp.c
+++ b/drivers/net/ethernet/mscc/ocelot_mrp.c
@@ -1,9 +1,6 @@
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/* Microsemi Ocelot Switch driver
*
- * This contains glue logic between the switchdev driver operations and the
- * mscc_ocelot_switch_lib.
- *
* Copyright (c) 2017, 2019 Microsemi Corporation
* Copyright 2020-2021 NXP Semiconductors
*/
@@ -15,13 +12,34 @@
#include "ocelot.h"
#include "ocelot_vcap.h"
-static int ocelot_mrp_del_vcap(struct ocelot *ocelot, int port)
+static const u8 mrp_test_dmac[] = { 0x01, 0x15, 0x4e, 0x00, 0x00, 0x01 };
+static const u8 mrp_control_dmac[] = { 0x01, 0x15, 0x4e, 0x00, 0x00, 0x02 };
+
+static int ocelot_mrp_find_partner_port(struct ocelot *ocelot,
+ struct ocelot_port *p)
+{
+ int i;
+
+ for (i = 0; i < ocelot->num_phys_ports; ++i) {
+ struct ocelot_port *ocelot_port = ocelot->ports[i];
+
+ if (!ocelot_port || p == ocelot_port)
+ continue;
+
+ if (ocelot_port->mrp_ring_id == p->mrp_ring_id)
+ return i;
+ }
+
+ return -1;
+}
+
+static int ocelot_mrp_del_vcap(struct ocelot *ocelot, int id)
{
struct ocelot_vcap_block *block_vcap_is2;
struct ocelot_vcap_filter *filter;
block_vcap_is2 = &ocelot->block[VCAP_IS2];
- filter = ocelot_vcap_block_find_filter_by_id(block_vcap_is2, port,
+ filter = ocelot_vcap_block_find_filter_by_id(block_vcap_is2, id,
false);
if (!filter)
return 0;
@@ -29,6 +47,87 @@ static int ocelot_mrp_del_vcap(struct ocelot *ocelot, int port)
return ocelot_vcap_filter_del(ocelot, filter);
}
+static int ocelot_mrp_redirect_add_vcap(struct ocelot *ocelot, int src_port,
+ int dst_port)
+{
+ const u8 mrp_test_mask[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+ struct ocelot_vcap_filter *filter;
+ int err;
+
+ filter = kzalloc(sizeof(*filter), GFP_KERNEL);
+ if (!filter)
+ return -ENOMEM;
+
+ filter->key_type = OCELOT_VCAP_KEY_ETYPE;
+ filter->prio = 1;
+ filter->id.cookie = src_port;
+ filter->id.tc_offload = false;
+ filter->block_id = VCAP_IS2;
+ filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
+ filter->ingress_port_mask = BIT(src_port);
+ ether_addr_copy(filter->key.etype.dmac.value, mrp_test_dmac);
+ ether_addr_copy(filter->key.etype.dmac.mask, mrp_test_mask);
+ filter->action.mask_mode = OCELOT_MASK_MODE_REDIRECT;
+ filter->action.port_mask = BIT(dst_port);
+
+ err = ocelot_vcap_filter_add(ocelot, filter, NULL);
+ if (err)
+ kfree(filter);
+
+ return err;
+}
+
+static int ocelot_mrp_copy_add_vcap(struct ocelot *ocelot, int port,
+ int prio, unsigned long cookie)
+{
+ const u8 mrp_mask[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 };
+ struct ocelot_vcap_filter *filter;
+ int err;
+
+ filter = kzalloc(sizeof(*filter), GFP_KERNEL);
+ if (!filter)
+ return -ENOMEM;
+
+ filter->key_type = OCELOT_VCAP_KEY_ETYPE;
+ filter->prio = prio;
+ filter->id.cookie = cookie;
+ filter->id.tc_offload = false;
+ filter->block_id = VCAP_IS2;
+ filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
+ filter->ingress_port_mask = BIT(port);
+ /* Here is possible to use control or test dmac because the mask
+ * doesn't cover the LSB
+ */
+ ether_addr_copy(filter->key.etype.dmac.value, mrp_test_dmac);
+ ether_addr_copy(filter->key.etype.dmac.mask, mrp_mask);
+ filter->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY;
+ filter->action.port_mask = 0x0;
+ filter->action.cpu_copy_ena = true;
+ filter->action.cpu_qu_num = OCELOT_MRP_CPUQ;
+
+ err = ocelot_vcap_filter_add(ocelot, filter, NULL);
+ if (err)
+ kfree(filter);
+
+ return err;
+}
+
+static void ocelot_mrp_save_mac(struct ocelot *ocelot,
+ struct ocelot_port *port)
+{
+ ocelot_mact_learn(ocelot, PGID_BLACKHOLE, mrp_test_dmac,
+ port->pvid_vlan.vid, ENTRYTYPE_LOCKED);
+ ocelot_mact_learn(ocelot, PGID_BLACKHOLE, mrp_control_dmac,
+ port->pvid_vlan.vid, ENTRYTYPE_LOCKED);
+}
+
+static void ocelot_mrp_del_mac(struct ocelot *ocelot,
+ struct ocelot_port *port)
+{
+ ocelot_mact_forget(ocelot, mrp_test_dmac, port->pvid_vlan.vid);
+ ocelot_mact_forget(ocelot, mrp_control_dmac, port->pvid_vlan.vid);
+}
+
int ocelot_mrp_add(struct ocelot *ocelot, int port,
const struct switchdev_obj_mrp *mrp)
{
@@ -45,18 +144,7 @@ int ocelot_mrp_add(struct ocelot *ocelot, int port,
if (mrp->p_port != dev && mrp->s_port != dev)
return 0;
- if (ocelot->mrp_ring_id != 0 &&
- ocelot->mrp_s_port &&
- ocelot->mrp_p_port)
- return -EINVAL;
-
- if (mrp->p_port == dev)
- ocelot->mrp_p_port = dev;
-
- if (mrp->s_port == dev)
- ocelot->mrp_s_port = dev;
-
- ocelot->mrp_ring_id = mrp->ring_id;
+ ocelot_port->mrp_ring_id = mrp->ring_id;
return 0;
}
@@ -66,34 +154,31 @@ int ocelot_mrp_del(struct ocelot *ocelot, int port,
const struct switchdev_obj_mrp *mrp)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
- struct ocelot_port_private *priv;
- struct net_device *dev;
+ int i;
if (!ocelot_port)
return -EOPNOTSUPP;
- priv = container_of(ocelot_port, struct ocelot_port_private, port);
- dev = priv->dev;
-
- if (ocelot->mrp_p_port != dev && ocelot->mrp_s_port != dev)
+ if (ocelot_port->mrp_ring_id != mrp->ring_id)
return 0;
- if (ocelot->mrp_ring_id == 0 &&
- !ocelot->mrp_s_port &&
- !ocelot->mrp_p_port)
- return -EINVAL;
+ ocelot_mrp_del_vcap(ocelot, port);
+ ocelot_mrp_del_vcap(ocelot, port + ocelot->num_phys_ports);
- if (ocelot_mrp_del_vcap(ocelot, priv->chip_port))
- return -EINVAL;
+ ocelot_port->mrp_ring_id = 0;
- if (ocelot->mrp_p_port == dev)
- ocelot->mrp_p_port = NULL;
+ for (i = 0; i < ocelot->num_phys_ports; ++i) {
+ ocelot_port = ocelot->ports[i];
- if (ocelot->mrp_s_port == dev)
- ocelot->mrp_s_port = NULL;
+ if (!ocelot_port)
+ continue;
- ocelot->mrp_ring_id = 0;
+ if (ocelot_port->mrp_ring_id != 0)
+ goto out;
+ }
+ ocelot_mrp_del_mac(ocelot, ocelot_port);
+out:
return 0;
}
EXPORT_SYMBOL(ocelot_mrp_del);
@@ -102,49 +187,39 @@ int ocelot_mrp_add_ring_role(struct ocelot *ocelot, int port,
const struct switchdev_obj_ring_role_mrp *mrp)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
- struct ocelot_vcap_filter *filter;
- struct ocelot_port_private *priv;
- struct net_device *dev;
+ int dst_port;
int err;
if (!ocelot_port)
return -EOPNOTSUPP;
- priv = container_of(ocelot_port, struct ocelot_port_private, port);
- dev = priv->dev;
-
- if (ocelot->mrp_ring_id != mrp->ring_id)
- return -EINVAL;
-
- if (!mrp->sw_backup)
+ if (mrp->ring_role != BR_MRP_RING_ROLE_MRC && !mrp->sw_backup)
return -EOPNOTSUPP;
- if (ocelot->mrp_p_port != dev && ocelot->mrp_s_port != dev)
+ if (ocelot_port->mrp_ring_id != mrp->ring_id)
return 0;
- filter = kzalloc(sizeof(*filter), GFP_ATOMIC);
- if (!filter)
- return -ENOMEM;
+ ocelot_mrp_save_mac(ocelot, ocelot_port);
- filter->key_type = OCELOT_VCAP_KEY_ETYPE;
- filter->prio = 1;
- filter->id.cookie = priv->chip_port;
- filter->id.tc_offload = false;
- filter->block_id = VCAP_IS2;
- filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
- filter->ingress_port_mask = BIT(priv->chip_port);
- *(__be16 *)filter->key.etype.etype.value = htons(ETH_P_MRP);
- *(__be16 *)filter->key.etype.etype.mask = htons(0xffff);
- filter->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY;
- filter->action.port_mask = 0x0;
- filter->action.cpu_copy_ena = true;
- filter->action.cpu_qu_num = OCELOT_MRP_CPUQ;
+ if (mrp->ring_role != BR_MRP_RING_ROLE_MRC)
+ return ocelot_mrp_copy_add_vcap(ocelot, port, 1, port);
- err = ocelot_vcap_filter_add(ocelot, filter, NULL);
+ dst_port = ocelot_mrp_find_partner_port(ocelot, ocelot_port);
+ if (dst_port == -1)
+ return -EINVAL;
+
+ err = ocelot_mrp_redirect_add_vcap(ocelot, port, dst_port);
if (err)
- kfree(filter);
+ return err;
- return err;
+ err = ocelot_mrp_copy_add_vcap(ocelot, port, 2,
+ port + ocelot->num_phys_ports);
+ if (err) {
+ ocelot_mrp_del_vcap(ocelot, port);
+ return err;
+ }
+
+ return 0;
}
EXPORT_SYMBOL(ocelot_mrp_add_ring_role);
@@ -152,24 +227,32 @@ int ocelot_mrp_del_ring_role(struct ocelot *ocelot, int port,
const struct switchdev_obj_ring_role_mrp *mrp)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
- struct ocelot_port_private *priv;
- struct net_device *dev;
+ int i;
if (!ocelot_port)
return -EOPNOTSUPP;
- priv = container_of(ocelot_port, struct ocelot_port_private, port);
- dev = priv->dev;
-
- if (ocelot->mrp_ring_id != mrp->ring_id)
- return -EINVAL;
-
- if (!mrp->sw_backup)
+ if (mrp->ring_role != BR_MRP_RING_ROLE_MRC && !mrp->sw_backup)
return -EOPNOTSUPP;
- if (ocelot->mrp_p_port != dev && ocelot->mrp_s_port != dev)
+ if (ocelot_port->mrp_ring_id != mrp->ring_id)
return 0;
- return ocelot_mrp_del_vcap(ocelot, priv->chip_port);
+ ocelot_mrp_del_vcap(ocelot, port);
+ ocelot_mrp_del_vcap(ocelot, port + ocelot->num_phys_ports);
+
+ for (i = 0; i < ocelot->num_phys_ports; ++i) {
+ ocelot_port = ocelot->ports[i];
+
+ if (!ocelot_port)
+ continue;
+
+ if (ocelot_port->mrp_ring_id != 0)
+ goto out;
+ }
+
+ ocelot_mrp_del_mac(ocelot, ocelot_port);
+out:
+ return 0;
}
EXPORT_SYMBOL(ocelot_mrp_del_ring_role);
diff --git a/include/linux/dsa/ocelot.h b/include/linux/dsa/ocelot.h
index 4265f328681a..c6bc45ae5e03 100644
--- a/include/linux/dsa/ocelot.h
+++ b/include/linux/dsa/ocelot.h
@@ -160,11 +160,6 @@ static inline void ocelot_xfh_get_src_port(void *extraction, u64 *src_port)
packing(extraction, src_port, 46, 43, OCELOT_TAG_LEN, UNPACK, 0);
}
-static inline void ocelot_xfh_get_cpuq(void *extraction, u64 *cpuq)
-{
- packing(extraction, cpuq, 28, 20, OCELOT_TAG_LEN, UNPACK, 0);
-}
-
static inline void ocelot_xfh_get_qos_class(void *extraction, u64 *qos_class)
{
packing(extraction, qos_class, 19, 17, OCELOT_TAG_LEN, UNPACK, 0);
diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
index 425ff29d9389..0a0751bf97dd 100644
--- a/include/soc/mscc/ocelot.h
+++ b/include/soc/mscc/ocelot.h
@@ -51,6 +51,7 @@
*/
/* Reserve some destination PGIDs at the end of the range:
+ * PGID_BLACKHOLE: used for not forwarding the frames
* PGID_CPU: used for whitelisting certain MAC addresses, such as the addresses
* of the switch port net devices, towards the CPU port module.
* PGID_UC: the flooding destinations for unknown unicast traffic.
@@ -59,6 +60,7 @@
* PGID_MCIPV6: the flooding destinations for IPv6 multicast traffic.
* PGID_BC: the flooding destinations for broadcast traffic.
*/
+#define PGID_BLACKHOLE 57
#define PGID_CPU 58
#define PGID_UC 59
#define PGID_MC 60
@@ -73,7 +75,7 @@
#define for_each_nonreserved_multicast_dest_pgid(ocelot, pgid) \
for ((pgid) = (ocelot)->num_phys_ports + 1; \
- (pgid) < PGID_CPU; \
+ (pgid) < PGID_BLACKHOLE; \
(pgid)++)
#define for_each_aggr_pgid(ocelot, pgid) \
@@ -611,6 +613,8 @@ struct ocelot_port {
struct net_device *bond;
bool lag_tx_active;
+
+ u16 mrp_ring_id;
};
struct ocelot {
@@ -679,12 +683,6 @@ struct ocelot {
/* Protects the PTP clock */
spinlock_t ptp_clock_lock;
struct ptp_pin_desc ptp_pins[OCELOT_PTP_PINS_NUM];
-
-#if IS_ENABLED(CONFIG_BRIDGE_MRP)
- u16 mrp_ring_id;
- struct net_device *mrp_p_port;
- struct net_device *mrp_s_port;
-#endif
};
struct ocelot_policer {
diff --git a/net/dsa/tag_ocelot.c b/net/dsa/tag_ocelot.c
index 743809b5806b..f9df9cac81c5 100644
--- a/net/dsa/tag_ocelot.c
+++ b/net/dsa/tag_ocelot.c
@@ -83,7 +83,6 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb,
struct dsa_port *dp;
u8 *extraction;
u16 vlan_tpid;
- u64 cpuq;
/* Revert skb->data by the amount consumed by the DSA master,
* so it points to the beginning of the frame.
@@ -113,7 +112,6 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb,
ocelot_xfh_get_qos_class(extraction, &qos_class);
ocelot_xfh_get_tag_type(extraction, &tag_type);
ocelot_xfh_get_vlan_tci(extraction, &vlan_tci);
- ocelot_xfh_get_cpuq(extraction, &cpuq);
skb->dev = dsa_master_find_slave(netdev, 0, src_port);
if (!skb->dev)
@@ -128,12 +126,6 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb,
skb->offload_fwd_mark = 1;
skb->priority = qos_class;
-#if IS_ENABLED(CONFIG_BRIDGE_MRP)
- if (eth_hdr(skb)->h_proto == cpu_to_be16(ETH_P_MRP) &&
- cpuq & BIT(OCELOT_MRP_CPUQ))
- skb->offload_fwd_mark = 0;
-#endif
-
/* Ocelot switches copy frames unmodified to the CPU. However, it is
* possible for the user to request a VLAN modification through
* VCAP_IS1_ACT_VID_REPLACE_ENA. In this case, what will happen is that