From e3c02b7c655cdfff240a7b8bfeff5ea06c006e12 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 16 Feb 2022 16:30:04 +0200 Subject: net: mscc: ocelot: use a consistent cookie for MRP traps The driver uses an identifier equal to (ocelot->num_phys_ports + port) for MRP traps installed when the system is in the role of an MRC, and an identifier equal to (port) otherwise. Use the same identifier in both cases as a consolidation for the various cookie values spread throughout the driver. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/ethernet/mscc/ocelot_mrp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mscc/ocelot_mrp.c b/drivers/net/ethernet/mscc/ocelot_mrp.c index 1fa58546abdc..742242bab6ef 100644 --- a/drivers/net/ethernet/mscc/ocelot_mrp.c +++ b/drivers/net/ethernet/mscc/ocelot_mrp.c @@ -186,7 +186,8 @@ int ocelot_mrp_add_ring_role(struct ocelot *ocelot, int port, ocelot_mrp_save_mac(ocelot, ocelot_port); if (mrp->ring_role != BR_MRP_RING_ROLE_MRC) - return ocelot_mrp_copy_add_vcap(ocelot, port, 1, port); + return ocelot_mrp_copy_add_vcap(ocelot, port, 1, + port + ocelot->num_phys_ports); dst_port = ocelot_mrp_find_partner_port(ocelot, ocelot_port); if (dst_port == -1) -- cgit v1.2.3 From c518afec288351347dbe05ea3d49d18fb9a9fff1 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 16 Feb 2022 16:30:05 +0200 Subject: net: mscc: ocelot: consolidate cookie allocation for private VCAP rules Every use case that needed VCAP filters (in order: DSA tag_8021q, MRP, PTP traps) has hardcoded filter identifiers that worked well enough for that use case alone. But when two or more of those use cases would be used together, some of those identifiers would overlap, leading to breakage. Add definitions for each cookie and centralize them in ocelot_vcap.h, such that the overlaps are more obvious. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/dsa/ocelot/felix.c | 10 +++++----- drivers/net/ethernet/mscc/ocelot.c | 20 ++++++++++---------- drivers/net/ethernet/mscc/ocelot_mrp.c | 20 +++++++++----------- include/soc/mscc/ocelot_vcap.h | 16 ++++++++++++++++ 4 files changed, 40 insertions(+), 26 deletions(-) diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 9957772201d5..c26f781f8df2 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -50,7 +50,7 @@ static int felix_tag_8021q_rxvlan_add(struct felix *felix, int port, u16 vid, outer_tagging_rule->key_type = OCELOT_VCAP_KEY_ANY; outer_tagging_rule->prio = 1; - outer_tagging_rule->id.cookie = port; + outer_tagging_rule->id.cookie = OCELOT_VCAP_ES0_TAG_8021Q_RXVLAN(ocelot, port); outer_tagging_rule->id.tc_offload = false; outer_tagging_rule->block_id = VCAP_ES0; outer_tagging_rule->type = OCELOT_VCAP_FILTER_OFFLOAD; @@ -103,7 +103,7 @@ static int felix_tag_8021q_txvlan_add(struct felix *felix, int port, u16 vid, untagging_rule->vlan.vid.value = vid; untagging_rule->vlan.vid.mask = VLAN_VID_MASK; untagging_rule->prio = 1; - untagging_rule->id.cookie = port; + untagging_rule->id.cookie = OCELOT_VCAP_IS1_TAG_8021Q_TXVLAN(ocelot, port); untagging_rule->id.tc_offload = false; untagging_rule->block_id = VCAP_IS1; untagging_rule->type = OCELOT_VCAP_FILTER_OFFLOAD; @@ -124,7 +124,7 @@ static int felix_tag_8021q_txvlan_add(struct felix *felix, int port, u16 vid, redirect_rule->ingress_port_mask = BIT(upstream); redirect_rule->pag = port; redirect_rule->prio = 1; - redirect_rule->id.cookie = port; + redirect_rule->id.cookie = OCELOT_VCAP_IS2_TAG_8021Q_TXVLAN(ocelot, port); redirect_rule->id.tc_offload = false; redirect_rule->block_id = VCAP_IS2; redirect_rule->type = OCELOT_VCAP_FILTER_OFFLOAD; @@ -308,7 +308,7 @@ static int felix_setup_mmio_filtering(struct felix *felix) *(__be16 *)tagging_rule->key.etype.etype.mask = htons(0xffff); tagging_rule->ingress_port_mask = user_ports; tagging_rule->prio = 1; - tagging_rule->id.cookie = ocelot->num_phys_ports; + tagging_rule->id.cookie = OCELOT_VCAP_IS1_TAG_8021Q_PTP_MMIO(ocelot); tagging_rule->id.tc_offload = false; tagging_rule->block_id = VCAP_IS1; tagging_rule->type = OCELOT_VCAP_FILTER_OFFLOAD; @@ -327,7 +327,7 @@ static int felix_setup_mmio_filtering(struct felix *felix) redirect_rule->ingress_port_mask = user_ports; redirect_rule->pag = ocelot->num_phys_ports; redirect_rule->prio = 1; - redirect_rule->id.cookie = ocelot->num_phys_ports; + redirect_rule->id.cookie = OCELOT_VCAP_IS2_TAG_8021Q_PTP_MMIO(ocelot); redirect_rule->id.tc_offload = false; redirect_rule->block_id = VCAP_IS2; redirect_rule->type = OCELOT_VCAP_FILTER_OFFLOAD; diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index 637fd79402e0..6039c9cb09c7 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -1537,7 +1537,7 @@ static int ocelot_trap_del(struct ocelot *ocelot, int port, static int ocelot_l2_ptp_trap_add(struct ocelot *ocelot, int port) { - unsigned long l2_cookie = ocelot->num_phys_ports + 1; + unsigned long l2_cookie = OCELOT_VCAP_IS2_L2_PTP_TRAP(ocelot); return ocelot_trap_add(ocelot, port, l2_cookie, ocelot_populate_l2_ptp_trap_key); @@ -1545,15 +1545,15 @@ static int ocelot_l2_ptp_trap_add(struct ocelot *ocelot, int port) static int ocelot_l2_ptp_trap_del(struct ocelot *ocelot, int port) { - unsigned long l2_cookie = ocelot->num_phys_ports + 1; + unsigned long l2_cookie = OCELOT_VCAP_IS2_L2_PTP_TRAP(ocelot); return ocelot_trap_del(ocelot, port, l2_cookie); } static int ocelot_ipv4_ptp_trap_add(struct ocelot *ocelot, int port) { - unsigned long ipv4_gen_cookie = ocelot->num_phys_ports + 2; - unsigned long ipv4_ev_cookie = ocelot->num_phys_ports + 3; + unsigned long ipv4_gen_cookie = OCELOT_VCAP_IS2_IPV4_GEN_PTP_TRAP(ocelot); + unsigned long ipv4_ev_cookie = OCELOT_VCAP_IS2_IPV4_EV_PTP_TRAP(ocelot); int err; err = ocelot_trap_add(ocelot, port, ipv4_ev_cookie, @@ -1571,8 +1571,8 @@ static int ocelot_ipv4_ptp_trap_add(struct ocelot *ocelot, int port) static int ocelot_ipv4_ptp_trap_del(struct ocelot *ocelot, int port) { - unsigned long ipv4_gen_cookie = ocelot->num_phys_ports + 2; - unsigned long ipv4_ev_cookie = ocelot->num_phys_ports + 3; + unsigned long ipv4_gen_cookie = OCELOT_VCAP_IS2_IPV4_GEN_PTP_TRAP(ocelot); + unsigned long ipv4_ev_cookie = OCELOT_VCAP_IS2_IPV4_EV_PTP_TRAP(ocelot); int err; err = ocelot_trap_del(ocelot, port, ipv4_ev_cookie); @@ -1582,8 +1582,8 @@ static int ocelot_ipv4_ptp_trap_del(struct ocelot *ocelot, int port) static int ocelot_ipv6_ptp_trap_add(struct ocelot *ocelot, int port) { - unsigned long ipv6_gen_cookie = ocelot->num_phys_ports + 4; - unsigned long ipv6_ev_cookie = ocelot->num_phys_ports + 5; + unsigned long ipv6_gen_cookie = OCELOT_VCAP_IS2_IPV6_GEN_PTP_TRAP(ocelot); + unsigned long ipv6_ev_cookie = OCELOT_VCAP_IS2_IPV6_EV_PTP_TRAP(ocelot); int err; err = ocelot_trap_add(ocelot, port, ipv6_ev_cookie, @@ -1601,8 +1601,8 @@ static int ocelot_ipv6_ptp_trap_add(struct ocelot *ocelot, int port) static int ocelot_ipv6_ptp_trap_del(struct ocelot *ocelot, int port) { - unsigned long ipv6_gen_cookie = ocelot->num_phys_ports + 4; - unsigned long ipv6_ev_cookie = ocelot->num_phys_ports + 5; + unsigned long ipv6_gen_cookie = OCELOT_VCAP_IS2_IPV6_GEN_PTP_TRAP(ocelot); + unsigned long ipv6_ev_cookie = OCELOT_VCAP_IS2_IPV6_EV_PTP_TRAP(ocelot); int err; err = ocelot_trap_del(ocelot, port, ipv6_ev_cookie); diff --git a/drivers/net/ethernet/mscc/ocelot_mrp.c b/drivers/net/ethernet/mscc/ocelot_mrp.c index 742242bab6ef..d763fb32a56c 100644 --- a/drivers/net/ethernet/mscc/ocelot_mrp.c +++ b/drivers/net/ethernet/mscc/ocelot_mrp.c @@ -60,7 +60,7 @@ static int ocelot_mrp_redirect_add_vcap(struct ocelot *ocelot, int src_port, filter->key_type = OCELOT_VCAP_KEY_ETYPE; filter->prio = 1; - filter->id.cookie = src_port; + filter->id.cookie = OCELOT_VCAP_IS2_MRP_REDIRECT(ocelot, src_port); filter->id.tc_offload = false; filter->block_id = VCAP_IS2; filter->type = OCELOT_VCAP_FILTER_OFFLOAD; @@ -77,8 +77,7 @@ static int ocelot_mrp_redirect_add_vcap(struct ocelot *ocelot, int src_port, return err; } -static int ocelot_mrp_copy_add_vcap(struct ocelot *ocelot, int port, - int prio, unsigned long cookie) +static int ocelot_mrp_copy_add_vcap(struct ocelot *ocelot, int port, int prio) { const u8 mrp_mask[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; struct ocelot_vcap_filter *filter; @@ -90,7 +89,7 @@ static int ocelot_mrp_copy_add_vcap(struct ocelot *ocelot, int port, filter->key_type = OCELOT_VCAP_KEY_ETYPE; filter->prio = prio; - filter->id.cookie = cookie; + filter->id.cookie = OCELOT_VCAP_IS2_MRP_TRAP(ocelot, port); filter->id.tc_offload = false; filter->block_id = VCAP_IS2; filter->type = OCELOT_VCAP_FILTER_OFFLOAD; @@ -186,8 +185,7 @@ int ocelot_mrp_add_ring_role(struct ocelot *ocelot, int port, ocelot_mrp_save_mac(ocelot, ocelot_port); if (mrp->ring_role != BR_MRP_RING_ROLE_MRC) - return ocelot_mrp_copy_add_vcap(ocelot, port, 1, - port + ocelot->num_phys_ports); + return ocelot_mrp_copy_add_vcap(ocelot, port, 1); dst_port = ocelot_mrp_find_partner_port(ocelot, ocelot_port); if (dst_port == -1) @@ -197,10 +195,10 @@ int ocelot_mrp_add_ring_role(struct ocelot *ocelot, int port, if (err) return err; - err = ocelot_mrp_copy_add_vcap(ocelot, port, 2, - port + ocelot->num_phys_ports); + err = ocelot_mrp_copy_add_vcap(ocelot, port, 2); if (err) { - ocelot_mrp_del_vcap(ocelot, port); + ocelot_mrp_del_vcap(ocelot, + OCELOT_VCAP_IS2_MRP_REDIRECT(ocelot, port)); return err; } @@ -223,8 +221,8 @@ int ocelot_mrp_del_ring_role(struct ocelot *ocelot, int port, if (ocelot_port->mrp_ring_id != mrp->ring_id) return 0; - ocelot_mrp_del_vcap(ocelot, port); - ocelot_mrp_del_vcap(ocelot, port + ocelot->num_phys_ports); + ocelot_mrp_del_vcap(ocelot, OCELOT_VCAP_IS2_MRP_REDIRECT(ocelot, port)); + ocelot_mrp_del_vcap(ocelot, OCELOT_VCAP_IS2_MRP_TRAP(ocelot, port)); for (i = 0; i < ocelot->num_phys_ports; ++i) { ocelot_port = ocelot->ports[i]; diff --git a/include/soc/mscc/ocelot_vcap.h b/include/soc/mscc/ocelot_vcap.h index 709cbc198fd2..562bcd972132 100644 --- a/include/soc/mscc/ocelot_vcap.h +++ b/include/soc/mscc/ocelot_vcap.h @@ -8,6 +8,22 @@ #include +/* Cookie definitions for private VCAP filters installed by the driver. + * Must be unique per VCAP block. + */ +#define OCELOT_VCAP_ES0_TAG_8021Q_RXVLAN(ocelot, port) (port) +#define OCELOT_VCAP_IS1_TAG_8021Q_TXVLAN(ocelot, port) (port) +#define OCELOT_VCAP_IS1_TAG_8021Q_PTP_MMIO(ocelot) ((ocelot)->num_phys_ports) +#define OCELOT_VCAP_IS2_TAG_8021Q_TXVLAN(ocelot, port) (port) +#define OCELOT_VCAP_IS2_TAG_8021Q_PTP_MMIO(ocelot) ((ocelot)->num_phys_ports) +#define OCELOT_VCAP_IS2_L2_PTP_TRAP(ocelot) ((ocelot)->num_phys_ports + 1) +#define OCELOT_VCAP_IS2_IPV4_GEN_PTP_TRAP(ocelot) ((ocelot)->num_phys_ports + 2) +#define OCELOT_VCAP_IS2_IPV4_EV_PTP_TRAP(ocelot) ((ocelot)->num_phys_ports + 3) +#define OCELOT_VCAP_IS2_IPV6_GEN_PTP_TRAP(ocelot) ((ocelot)->num_phys_ports + 4) +#define OCELOT_VCAP_IS2_IPV6_EV_PTP_TRAP(ocelot) ((ocelot)->num_phys_ports + 5) +#define OCELOT_VCAP_IS2_MRP_REDIRECT(ocelot, port) (port) +#define OCELOT_VCAP_IS2_MRP_TRAP(ocelot, port) ((ocelot)->num_phys_ports + (port)) + /* ================================================================= * VCAP Common * ================================================================= -- cgit v1.2.3 From 36fac35b29072e345d5fc485cf7841be265181b1 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 16 Feb 2022 16:30:06 +0200 Subject: net: mscc: ocelot: delete OCELOT_MRP_CPUQ MRP frames are configured to be trapped to the CPU queue 7, and this number is reflected in the extraction header. However, the information isn't used anywhere, so just leave MRP frames to go to CPU queue 0 unless needed otherwise. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/ethernet/mscc/ocelot_mrp.c | 1 - include/soc/mscc/ocelot.h | 2 -- 2 files changed, 3 deletions(-) diff --git a/drivers/net/ethernet/mscc/ocelot_mrp.c b/drivers/net/ethernet/mscc/ocelot_mrp.c index d763fb32a56c..dc28736e2eb3 100644 --- a/drivers/net/ethernet/mscc/ocelot_mrp.c +++ b/drivers/net/ethernet/mscc/ocelot_mrp.c @@ -102,7 +102,6 @@ static int ocelot_mrp_copy_add_vcap(struct ocelot *ocelot, int port, int prio) 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) diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h index cacb103e4bad..2d7456c0e77d 100644 --- a/include/soc/mscc/ocelot.h +++ b/include/soc/mscc/ocelot.h @@ -105,8 +105,6 @@ #define REG_RESERVED_ADDR 0xffffffff #define REG_RESERVED(reg) REG(reg, REG_RESERVED_ADDR) -#define OCELOT_MRP_CPUQ 7 - enum ocelot_target { ANA = 1, QS, -- cgit v1.2.3 From b9bace6e534d431871a9d69cbd06d3a798f5086d Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 16 Feb 2022 16:30:07 +0200 Subject: net: mscc: ocelot: use a single VCAP filter for all MRP traps The MRP assist code installs a VCAP IS2 trapping rule for each port, but since the key and the action is the same, just the ingress port mask differs, there isn't any need to do this. We can save some space in the TCAM by using a single filter and adjusting the ingress port mask. Reuse the ocelot_trap_add() and ocelot_trap_del() functions for this purpose. Now that the cookies are no longer per port, we need to change the allocation scheme such that MRP traps use a fixed number. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/ethernet/mscc/ocelot.c | 8 +++--- drivers/net/ethernet/mscc/ocelot.h | 5 ++++ drivers/net/ethernet/mscc/ocelot_mrp.c | 46 ++++++++++++++++------------------ include/soc/mscc/ocelot_vcap.h | 2 +- 4 files changed, 30 insertions(+), 31 deletions(-) diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index 6039c9cb09c7..502e64764cbd 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -1468,9 +1468,8 @@ ocelot_populate_ipv6_ptp_general_trap_key(struct ocelot_vcap_filter *trap) trap->key.ipv6.dport.mask = 0xffff; } -static int ocelot_trap_add(struct ocelot *ocelot, int port, - unsigned long cookie, - void (*populate)(struct ocelot_vcap_filter *f)) +int ocelot_trap_add(struct ocelot *ocelot, int port, unsigned long cookie, + void (*populate)(struct ocelot_vcap_filter *f)) { struct ocelot_vcap_block *block_vcap_is2; struct ocelot_vcap_filter *trap; @@ -1515,8 +1514,7 @@ static int ocelot_trap_add(struct ocelot *ocelot, int port, return 0; } -static int ocelot_trap_del(struct ocelot *ocelot, int port, - unsigned long cookie) +int ocelot_trap_del(struct ocelot *ocelot, int port, unsigned long cookie) { struct ocelot_vcap_block *block_vcap_is2; struct ocelot_vcap_filter *trap; diff --git a/drivers/net/ethernet/mscc/ocelot.h b/drivers/net/ethernet/mscc/ocelot.h index bf4eff6d7086..674043cd9088 100644 --- a/drivers/net/ethernet/mscc/ocelot.h +++ b/drivers/net/ethernet/mscc/ocelot.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include "ocelot_rew.h" #include "ocelot_qs.h" @@ -102,6 +103,10 @@ int ocelot_port_devlink_init(struct ocelot *ocelot, int port, enum devlink_port_flavour flavour); void ocelot_port_devlink_teardown(struct ocelot *ocelot, int port); +int ocelot_trap_add(struct ocelot *ocelot, int port, unsigned long cookie, + void (*populate)(struct ocelot_vcap_filter *f)); +int ocelot_trap_del(struct ocelot *ocelot, int port, unsigned long cookie); + extern struct notifier_block ocelot_netdevice_nb; extern struct notifier_block ocelot_switchdev_nb; extern struct notifier_block ocelot_switchdev_blocking_nb; diff --git a/drivers/net/ethernet/mscc/ocelot_mrp.c b/drivers/net/ethernet/mscc/ocelot_mrp.c index dc28736e2eb3..68fa833f4aaa 100644 --- a/drivers/net/ethernet/mscc/ocelot_mrp.c +++ b/drivers/net/ethernet/mscc/ocelot_mrp.c @@ -77,37 +77,30 @@ static int ocelot_mrp_redirect_add_vcap(struct ocelot *ocelot, int src_port, return err; } -static int ocelot_mrp_copy_add_vcap(struct ocelot *ocelot, int port, int prio) +static void ocelot_populate_mrp_trap_key(struct ocelot_vcap_filter *filter) { 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 = OCELOT_VCAP_IS2_MRP_TRAP(ocelot, port); - 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; +} - err = ocelot_vcap_filter_add(ocelot, filter, NULL); - if (err) - kfree(filter); +static int ocelot_mrp_trap_add(struct ocelot *ocelot, int port) +{ + unsigned long cookie = OCELOT_VCAP_IS2_MRP_TRAP(ocelot); - return err; + return ocelot_trap_add(ocelot, port, cookie, + ocelot_populate_mrp_trap_key); +} + +static int ocelot_mrp_trap_del(struct ocelot *ocelot, int port) +{ + unsigned long cookie = OCELOT_VCAP_IS2_MRP_TRAP(ocelot); + + return ocelot_trap_del(ocelot, port, cookie); } static void ocelot_mrp_save_mac(struct ocelot *ocelot, @@ -184,7 +177,7 @@ int ocelot_mrp_add_ring_role(struct ocelot *ocelot, int port, ocelot_mrp_save_mac(ocelot, ocelot_port); if (mrp->ring_role != BR_MRP_RING_ROLE_MRC) - return ocelot_mrp_copy_add_vcap(ocelot, port, 1); + return ocelot_mrp_trap_add(ocelot, port); dst_port = ocelot_mrp_find_partner_port(ocelot, ocelot_port); if (dst_port == -1) @@ -194,7 +187,7 @@ int ocelot_mrp_add_ring_role(struct ocelot *ocelot, int port, if (err) return err; - err = ocelot_mrp_copy_add_vcap(ocelot, port, 2); + err = ocelot_mrp_trap_add(ocelot, port); if (err) { ocelot_mrp_del_vcap(ocelot, OCELOT_VCAP_IS2_MRP_REDIRECT(ocelot, port)); @@ -209,7 +202,7 @@ 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]; - int i; + int err, i; if (!ocelot_port) return -EOPNOTSUPP; @@ -220,8 +213,11 @@ int ocelot_mrp_del_ring_role(struct ocelot *ocelot, int port, if (ocelot_port->mrp_ring_id != mrp->ring_id) return 0; + err = ocelot_mrp_trap_del(ocelot, port); + if (err) + return err; + ocelot_mrp_del_vcap(ocelot, OCELOT_VCAP_IS2_MRP_REDIRECT(ocelot, port)); - ocelot_mrp_del_vcap(ocelot, OCELOT_VCAP_IS2_MRP_TRAP(ocelot, port)); for (i = 0; i < ocelot->num_phys_ports; ++i) { ocelot_port = ocelot->ports[i]; diff --git a/include/soc/mscc/ocelot_vcap.h b/include/soc/mscc/ocelot_vcap.h index 562bcd972132..14ada097db0b 100644 --- a/include/soc/mscc/ocelot_vcap.h +++ b/include/soc/mscc/ocelot_vcap.h @@ -21,8 +21,8 @@ #define OCELOT_VCAP_IS2_IPV4_EV_PTP_TRAP(ocelot) ((ocelot)->num_phys_ports + 3) #define OCELOT_VCAP_IS2_IPV6_GEN_PTP_TRAP(ocelot) ((ocelot)->num_phys_ports + 4) #define OCELOT_VCAP_IS2_IPV6_EV_PTP_TRAP(ocelot) ((ocelot)->num_phys_ports + 5) +#define OCELOT_VCAP_IS2_MRP_TRAP(ocelot) ((ocelot)->num_phys_ports + 6) #define OCELOT_VCAP_IS2_MRP_REDIRECT(ocelot, port) (port) -#define OCELOT_VCAP_IS2_MRP_TRAP(ocelot, port) ((ocelot)->num_phys_ports + (port)) /* ================================================================= * VCAP Common -- cgit v1.2.3 From 85ea0daabe5abc6add440f86565b7b9f6aff5535 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 16 Feb 2022 16:30:08 +0200 Subject: net: mscc: ocelot: avoid overlap in VCAP IS2 between PTP and MRP traps OCELOT_VCAP_IS2_TAG_8021Q_TXVLAN overlaps with OCELOT_VCAP_IS2_MRP_REDIRECT. To avoid this, make OCELOT_VCAP_IS2_MRP_REDIRECT take the cookie region from N to 2 * N - 1 (where N is ocelot->num_phys_ports). To avoid any risk that the singleton (not per port) VCAP IS2 filters overlap with per-port VCAP IS2 filters, we must ensure that the number of singleton filters is smaller than the number of physical ports. This is true right now, but may change in the future as switches with less ports get supported, or more singleton filters get added. So to be future-proof, let's move the singleton filters at the end of the range, where they won't overlap with anything to their right. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- include/soc/mscc/ocelot_vcap.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/soc/mscc/ocelot_vcap.h b/include/soc/mscc/ocelot_vcap.h index 14ada097db0b..ae0eec7f5dd2 100644 --- a/include/soc/mscc/ocelot_vcap.h +++ b/include/soc/mscc/ocelot_vcap.h @@ -15,14 +15,14 @@ #define OCELOT_VCAP_IS1_TAG_8021Q_TXVLAN(ocelot, port) (port) #define OCELOT_VCAP_IS1_TAG_8021Q_PTP_MMIO(ocelot) ((ocelot)->num_phys_ports) #define OCELOT_VCAP_IS2_TAG_8021Q_TXVLAN(ocelot, port) (port) -#define OCELOT_VCAP_IS2_TAG_8021Q_PTP_MMIO(ocelot) ((ocelot)->num_phys_ports) -#define OCELOT_VCAP_IS2_L2_PTP_TRAP(ocelot) ((ocelot)->num_phys_ports + 1) -#define OCELOT_VCAP_IS2_IPV4_GEN_PTP_TRAP(ocelot) ((ocelot)->num_phys_ports + 2) -#define OCELOT_VCAP_IS2_IPV4_EV_PTP_TRAP(ocelot) ((ocelot)->num_phys_ports + 3) -#define OCELOT_VCAP_IS2_IPV6_GEN_PTP_TRAP(ocelot) ((ocelot)->num_phys_ports + 4) -#define OCELOT_VCAP_IS2_IPV6_EV_PTP_TRAP(ocelot) ((ocelot)->num_phys_ports + 5) -#define OCELOT_VCAP_IS2_MRP_TRAP(ocelot) ((ocelot)->num_phys_ports + 6) -#define OCELOT_VCAP_IS2_MRP_REDIRECT(ocelot, port) (port) +#define OCELOT_VCAP_IS2_MRP_REDIRECT(ocelot, port) ((ocelot)->num_phys_ports + (port)) +#define OCELOT_VCAP_IS2_TAG_8021Q_PTP_MMIO(ocelot) ((ocelot)->num_phys_ports * 2) +#define OCELOT_VCAP_IS2_L2_PTP_TRAP(ocelot) ((ocelot)->num_phys_ports * 2 + 1) +#define OCELOT_VCAP_IS2_IPV4_GEN_PTP_TRAP(ocelot) ((ocelot)->num_phys_ports * 2 + 2) +#define OCELOT_VCAP_IS2_IPV4_EV_PTP_TRAP(ocelot) ((ocelot)->num_phys_ports * 2 + 3) +#define OCELOT_VCAP_IS2_IPV6_GEN_PTP_TRAP(ocelot) ((ocelot)->num_phys_ports * 2 + 4) +#define OCELOT_VCAP_IS2_IPV6_EV_PTP_TRAP(ocelot) ((ocelot)->num_phys_ports * 2 + 5) +#define OCELOT_VCAP_IS2_MRP_TRAP(ocelot) ((ocelot)->num_phys_ports * 2 + 6) /* ================================================================= * VCAP Common -- cgit v1.2.3 From 2960bb14ea27e8ce48e05534eb6737de1176fe22 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 16 Feb 2022 16:30:09 +0200 Subject: net: dsa: felix: use DSA port iteration helpers Use the helpers that avoid the quadratic complexity associated with calling dsa_to_port() indirectly: dsa_is_unused_port(), dsa_is_cpu_port(). Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/dsa/ocelot/felix.c | 75 +++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 48 deletions(-) diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index c26f781f8df2..9c96746d3051 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -278,7 +278,8 @@ static int felix_setup_mmio_filtering(struct felix *felix) struct ocelot_vcap_filter *tagging_rule; struct ocelot *ocelot = &felix->ocelot; struct dsa_switch *ds = felix->ds; - int cpu = -1, port, ret; + struct dsa_port *dp; + int cpu = -1, ret; tagging_rule = kzalloc(sizeof(struct ocelot_vcap_filter), GFP_KERNEL); if (!tagging_rule) @@ -290,11 +291,9 @@ static int felix_setup_mmio_filtering(struct felix *felix) return -ENOMEM; } - for (port = 0; port < ocelot->num_phys_ports; port++) { - if (dsa_is_cpu_port(ds, port)) { - cpu = port; - break; - } + dsa_switch_for_each_cpu_port(dp, ds) { + cpu = dp->index; + break; } if (cpu < 0) { @@ -401,14 +400,12 @@ static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu) struct ocelot *ocelot = ds->priv; struct felix *felix = ocelot_to_felix(ocelot); unsigned long cpu_flood; - int port, err; + struct dsa_port *dp; + int err; felix_8021q_cpu_port_init(ocelot, cpu); - for (port = 0; port < ds->num_ports; port++) { - if (dsa_is_unused_port(ds, port)) - continue; - + dsa_switch_for_each_available_port(dp, ds) { /* This overwrites ocelot_init(): * Do not forward BPDU frames to the CPU port module, * for 2 reasons: @@ -421,7 +418,7 @@ static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu) */ ocelot_write_gix(ocelot, ANA_PORT_CPU_FWD_BPDU_CFG_BPDU_REDIR_ENA(0), - ANA_PORT_CPU_FWD_BPDU_CFG, port); + ANA_PORT_CPU_FWD_BPDU_CFG, dp->index); } /* In tag_8021q mode, the CPU port module is unused, except for PTP @@ -452,7 +449,8 @@ static void felix_teardown_tag_8021q(struct dsa_switch *ds, int cpu) { struct ocelot *ocelot = ds->priv; struct felix *felix = ocelot_to_felix(ocelot); - int err, port; + struct dsa_port *dp; + int err; err = felix_teardown_mmio_filtering(felix); if (err) @@ -461,17 +459,14 @@ static void felix_teardown_tag_8021q(struct dsa_switch *ds, int cpu) dsa_tag_8021q_unregister(ds); - for (port = 0; port < ds->num_ports; port++) { - if (dsa_is_unused_port(ds, port)) - continue; - + dsa_switch_for_each_available_port(dp, ds) { /* Restore the logic from ocelot_init: * do not forward BPDU frames to the front ports. */ ocelot_write_gix(ocelot, ANA_PORT_CPU_FWD_BPDU_CFG_BPDU_REDIR_ENA(0xffff), ANA_PORT_CPU_FWD_BPDU_CFG, - port); + dp->index); } felix_8021q_cpu_port_deinit(ocelot, cpu); @@ -1192,7 +1187,8 @@ static int felix_setup(struct dsa_switch *ds) { struct ocelot *ocelot = ds->priv; struct felix *felix = ocelot_to_felix(ocelot); - int port, err; + struct dsa_port *dp; + int err; err = felix_init_structs(felix, ds->num_ports); if (err) @@ -1211,30 +1207,24 @@ static int felix_setup(struct dsa_switch *ds) } } - for (port = 0; port < ds->num_ports; port++) { - if (dsa_is_unused_port(ds, port)) - continue; - - ocelot_init_port(ocelot, port); + dsa_switch_for_each_available_port(dp, ds) { + ocelot_init_port(ocelot, dp->index); /* Set the default QoS Classification based on PCP and DEI * bits of vlan tag. */ - felix_port_qos_map_init(ocelot, port); + felix_port_qos_map_init(ocelot, dp->index); } err = ocelot_devlink_sb_register(ocelot); if (err) goto out_deinit_ports; - for (port = 0; port < ds->num_ports; port++) { - if (!dsa_is_cpu_port(ds, port)) - continue; - + dsa_switch_for_each_cpu_port(dp, ds) { /* The initial tag protocol is NPI which always returns 0, so * there's no real point in checking for errors. */ - felix_set_tag_protocol(ds, port, felix->tag_proto); + felix_set_tag_protocol(ds, dp->index, felix->tag_proto); break; } @@ -1244,12 +1234,8 @@ static int felix_setup(struct dsa_switch *ds) return 0; out_deinit_ports: - for (port = 0; port < ocelot->num_phys_ports; port++) { - if (dsa_is_unused_port(ds, port)) - continue; - - ocelot_deinit_port(ocelot, port); - } + dsa_switch_for_each_available_port(dp, ds) + ocelot_deinit_port(ocelot, dp->index); ocelot_deinit_timestamp(ocelot); ocelot_deinit(ocelot); @@ -1265,22 +1251,15 @@ static void felix_teardown(struct dsa_switch *ds) { struct ocelot *ocelot = ds->priv; struct felix *felix = ocelot_to_felix(ocelot); - int port; - - for (port = 0; port < ds->num_ports; port++) { - if (!dsa_is_cpu_port(ds, port)) - continue; + struct dsa_port *dp; - felix_del_tag_protocol(ds, port, felix->tag_proto); + dsa_switch_for_each_cpu_port(dp, ds) { + felix_del_tag_protocol(ds, dp->index, felix->tag_proto); break; } - for (port = 0; port < ocelot->num_phys_ports; port++) { - if (dsa_is_unused_port(ds, port)) - continue; - - ocelot_deinit_port(ocelot, port); - } + dsa_switch_for_each_available_port(dp, ds) + ocelot_deinit_port(ocelot, dp->index); ocelot_devlink_sb_unregister(ocelot); ocelot_deinit_timestamp(ocelot); -- cgit v1.2.3 From e42bd4ed09aaf57949f199ae02d20a7108ccf73b Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 16 Feb 2022 16:30:10 +0200 Subject: net: mscc: ocelot: keep traps in a list When using the ocelot-8021q tagging protocol, the CPU port isn't configured as an NPI port, but is a regular port. So a "trap to CPU" operation is actually a "redirect" operation. So DSA needs to set up the trapping action one way or another, depending on the tagging protocol in use. To ease DSA's work of modifying the action, keep all currently installed traps in a list, so that DSA can live-patch them when the tagging protocol changes. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/ethernet/mscc/ocelot.c | 10 ++++++++-- drivers/net/ethernet/mscc/ocelot_flower.c | 3 +++ drivers/net/ethernet/mscc/ocelot_vcap.c | 1 + include/soc/mscc/ocelot.h | 1 + include/soc/mscc/ocelot_vcap.h | 1 + 5 files changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index 502e64764cbd..5356d9316943 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -1495,6 +1495,7 @@ int ocelot_trap_add(struct ocelot *ocelot, int port, unsigned long cookie, trap->action.cpu_copy_ena = true; trap->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY; trap->action.port_mask = 0; + list_add_tail(&trap->trap_list, &ocelot->traps); new = true; } @@ -1506,8 +1507,10 @@ int ocelot_trap_add(struct ocelot *ocelot, int port, unsigned long cookie, err = ocelot_vcap_filter_replace(ocelot, trap); if (err) { trap->ingress_port_mask &= ~BIT(port); - if (!trap->ingress_port_mask) + if (!trap->ingress_port_mask) { + list_del(&trap->trap_list); kfree(trap); + } return err; } @@ -1527,8 +1530,11 @@ int ocelot_trap_del(struct ocelot *ocelot, int port, unsigned long cookie) return 0; trap->ingress_port_mask &= ~BIT(port); - if (!trap->ingress_port_mask) + if (!trap->ingress_port_mask) { + list_del(&trap->trap_list); + return ocelot_vcap_filter_del(ocelot, trap); + } return ocelot_vcap_filter_replace(ocelot, trap); } diff --git a/drivers/net/ethernet/mscc/ocelot_flower.c b/drivers/net/ethernet/mscc/ocelot_flower.c index 949858891973..7106137f98ee 100644 --- a/drivers/net/ethernet/mscc/ocelot_flower.c +++ b/drivers/net/ethernet/mscc/ocelot_flower.c @@ -279,6 +279,7 @@ static int ocelot_flower_parse_action(struct ocelot *ocelot, int port, filter->action.cpu_copy_ena = true; filter->action.cpu_qu_num = 0; filter->type = OCELOT_VCAP_FILTER_OFFLOAD; + list_add_tail(&filter->trap_list, &ocelot->traps); break; case FLOW_ACTION_POLICE: if (filter->block_id == PSFP_BLOCK_ID) { @@ -840,6 +841,8 @@ int ocelot_cls_flower_replace(struct ocelot *ocelot, int port, ret = ocelot_flower_parse(ocelot, port, ingress, f, filter); if (ret) { + if (!list_empty(&filter->trap_list)) + list_del(&filter->trap_list); kfree(filter); return ret; } diff --git a/drivers/net/ethernet/mscc/ocelot_vcap.c b/drivers/net/ethernet/mscc/ocelot_vcap.c index d3544413a8a4..852054da9db9 100644 --- a/drivers/net/ethernet/mscc/ocelot_vcap.c +++ b/drivers/net/ethernet/mscc/ocelot_vcap.c @@ -1401,6 +1401,7 @@ int ocelot_vcap_init(struct ocelot *ocelot) } INIT_LIST_HEAD(&ocelot->dummy_rules); + INIT_LIST_HEAD(&ocelot->traps); INIT_LIST_HEAD(&ocelot->vcap_pol.pol_list); return 0; diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h index 2d7456c0e77d..78f56502bc09 100644 --- a/include/soc/mscc/ocelot.h +++ b/include/soc/mscc/ocelot.h @@ -689,6 +689,7 @@ struct ocelot { u8 base_mac[ETH_ALEN]; struct list_head vlans; + struct list_head traps; /* Switches like VSC9959 have flooding per traffic class */ int num_flooding_pgids; diff --git a/include/soc/mscc/ocelot_vcap.h b/include/soc/mscc/ocelot_vcap.h index ae0eec7f5dd2..69b3d880302d 100644 --- a/include/soc/mscc/ocelot_vcap.h +++ b/include/soc/mscc/ocelot_vcap.h @@ -682,6 +682,7 @@ struct ocelot_vcap_id { struct ocelot_vcap_filter { struct list_head list; + struct list_head trap_list; enum ocelot_vcap_filter_type type; int block_id; -- cgit v1.2.3 From 9d75b8818537fe64e6eae033765b9dc1b3107c15 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 16 Feb 2022 16:30:11 +0200 Subject: net: mscc: ocelot: annotate which traps need PTP timestamping The ocelot switch library does not need this information, but the felix DSA driver does. As a reminder, the VSC9959 switch in LS1028A doesn't have an IRQ line for packet extraction, so to be notified that a PTP packet needs to be dequeued, it receives that packet also over Ethernet, by setting up a packet trap. The Felix driver needs to install special kinds of traps for packets in need of RX timestamps, such that the packets are replicated both over Ethernet and over the CPU port module. But the Ocelot switch library sets up more than one trap for PTP event messages; it also traps PTP general messages, MRP control messages etc. Those packets don't need PTP timestamps, so there's no reason for the Felix driver to send them to the CPU port module. By knowing which traps need PTP timestamps, the Felix driver can adjust the traps installed using ocelot_trap_add() such that only those will actually get delivered to the CPU port module. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/ethernet/mscc/ocelot.c | 14 ++++++++------ drivers/net/ethernet/mscc/ocelot.h | 3 ++- drivers/net/ethernet/mscc/ocelot_mrp.c | 2 +- include/soc/mscc/ocelot_vcap.h | 1 + 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index 5356d9316943..26f8cf235504 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -1468,7 +1468,8 @@ ocelot_populate_ipv6_ptp_general_trap_key(struct ocelot_vcap_filter *trap) trap->key.ipv6.dport.mask = 0xffff; } -int ocelot_trap_add(struct ocelot *ocelot, int port, unsigned long cookie, +int ocelot_trap_add(struct ocelot *ocelot, int port, + unsigned long cookie, bool take_ts, void (*populate)(struct ocelot_vcap_filter *f)) { struct ocelot_vcap_block *block_vcap_is2; @@ -1495,6 +1496,7 @@ int ocelot_trap_add(struct ocelot *ocelot, int port, unsigned long cookie, trap->action.cpu_copy_ena = true; trap->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY; trap->action.port_mask = 0; + trap->take_ts = take_ts; list_add_tail(&trap->trap_list, &ocelot->traps); new = true; } @@ -1543,7 +1545,7 @@ static int ocelot_l2_ptp_trap_add(struct ocelot *ocelot, int port) { unsigned long l2_cookie = OCELOT_VCAP_IS2_L2_PTP_TRAP(ocelot); - return ocelot_trap_add(ocelot, port, l2_cookie, + return ocelot_trap_add(ocelot, port, l2_cookie, true, ocelot_populate_l2_ptp_trap_key); } @@ -1560,12 +1562,12 @@ static int ocelot_ipv4_ptp_trap_add(struct ocelot *ocelot, int port) unsigned long ipv4_ev_cookie = OCELOT_VCAP_IS2_IPV4_EV_PTP_TRAP(ocelot); int err; - err = ocelot_trap_add(ocelot, port, ipv4_ev_cookie, + err = ocelot_trap_add(ocelot, port, ipv4_ev_cookie, true, ocelot_populate_ipv4_ptp_event_trap_key); if (err) return err; - err = ocelot_trap_add(ocelot, port, ipv4_gen_cookie, + err = ocelot_trap_add(ocelot, port, ipv4_gen_cookie, false, ocelot_populate_ipv4_ptp_general_trap_key); if (err) ocelot_trap_del(ocelot, port, ipv4_ev_cookie); @@ -1590,12 +1592,12 @@ static int ocelot_ipv6_ptp_trap_add(struct ocelot *ocelot, int port) unsigned long ipv6_ev_cookie = OCELOT_VCAP_IS2_IPV6_EV_PTP_TRAP(ocelot); int err; - err = ocelot_trap_add(ocelot, port, ipv6_ev_cookie, + err = ocelot_trap_add(ocelot, port, ipv6_ev_cookie, true, ocelot_populate_ipv6_ptp_event_trap_key); if (err) return err; - err = ocelot_trap_add(ocelot, port, ipv6_gen_cookie, + err = ocelot_trap_add(ocelot, port, ipv6_gen_cookie, false, ocelot_populate_ipv6_ptp_general_trap_key); if (err) ocelot_trap_del(ocelot, port, ipv6_ev_cookie); diff --git a/drivers/net/ethernet/mscc/ocelot.h b/drivers/net/ethernet/mscc/ocelot.h index 674043cd9088..5277c4b53af4 100644 --- a/drivers/net/ethernet/mscc/ocelot.h +++ b/drivers/net/ethernet/mscc/ocelot.h @@ -103,7 +103,8 @@ int ocelot_port_devlink_init(struct ocelot *ocelot, int port, enum devlink_port_flavour flavour); void ocelot_port_devlink_teardown(struct ocelot *ocelot, int port); -int ocelot_trap_add(struct ocelot *ocelot, int port, unsigned long cookie, +int ocelot_trap_add(struct ocelot *ocelot, int port, + unsigned long cookie, bool take_ts, void (*populate)(struct ocelot_vcap_filter *f)); int ocelot_trap_del(struct ocelot *ocelot, int port, unsigned long cookie); diff --git a/drivers/net/ethernet/mscc/ocelot_mrp.c b/drivers/net/ethernet/mscc/ocelot_mrp.c index 68fa833f4aaa..142e897ea2af 100644 --- a/drivers/net/ethernet/mscc/ocelot_mrp.c +++ b/drivers/net/ethernet/mscc/ocelot_mrp.c @@ -92,7 +92,7 @@ static int ocelot_mrp_trap_add(struct ocelot *ocelot, int port) { unsigned long cookie = OCELOT_VCAP_IS2_MRP_TRAP(ocelot); - return ocelot_trap_add(ocelot, port, cookie, + return ocelot_trap_add(ocelot, port, cookie, false, ocelot_populate_mrp_trap_key); } diff --git a/include/soc/mscc/ocelot_vcap.h b/include/soc/mscc/ocelot_vcap.h index 69b3d880302d..50af64e2ca3c 100644 --- a/include/soc/mscc/ocelot_vcap.h +++ b/include/soc/mscc/ocelot_vcap.h @@ -695,6 +695,7 @@ struct ocelot_vcap_filter { struct ocelot_vcap_action action; struct ocelot_vcap_stats stats; /* For VCAP IS1 and IS2 */ + bool take_ts; unsigned long ingress_port_mask; /* For VCAP ES0 */ struct ocelot_vcap_port ingress_port; -- cgit v1.2.3 From d78637a8a061643f734e96642df308de52d91592 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 16 Feb 2022 16:30:12 +0200 Subject: net: dsa: felix: remove dead code in felix_setup_mmio_filtering() There has been some controversy related to the sanity check that a CPU port exists, and commit e8b1d7698038 ("net: dsa: felix: Fix memory leak in felix_setup_mmio_filtering") even "corrected" an apparent memory leak as static analysis tools see it. However, the check is completely dead code, since the earliest point at which felix_setup_mmio_filtering() can be called is: felix_pci_probe -> dsa_register_switch -> dsa_switch_probe -> dsa_tree_setup -> dsa_tree_setup_cpu_ports -> dsa_tree_setup_default_cpu -> contains the "DSA: tree %d has no CPU port\n" check -> dsa_tree_setup_master -> dsa_master_setup -> sysfs_create_group(&dev->dev.kobj, &dsa_group); -> makes tagging_store() callable -> dsa_tree_change_tag_proto -> dsa_tree_notify -> dsa_switch_event -> dsa_switch_change_tag_proto -> ds->ops->change_tag_protocol -> felix_change_tag_protocol -> felix_set_tag_protocol -> felix_setup_tag_8021q -> felix_setup_mmio_filtering -> breaks at first CPU port So probing would have failed earlier if there wasn't any CPU port defined. To avoid all confusion, delete the dead code and replace it with a comment. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/dsa/ocelot/felix.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 9c96746d3051..7293eff0e90a 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -296,11 +296,9 @@ static int felix_setup_mmio_filtering(struct felix *felix) break; } - if (cpu < 0) { - kfree(tagging_rule); - kfree(redirect_rule); - return -EINVAL; - } + /* We are sure that "cpu" was found, otherwise + * dsa_tree_setup_default_cpu() would have failed earlier. + */ tagging_rule->key_type = OCELOT_VCAP_KEY_ETYPE; *(__be16 *)tagging_rule->key.etype.etype.value = htons(ETH_P_1588); -- cgit v1.2.3 From 9934800436552d2a4af4aaca62d779b33d1f6a63 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 16 Feb 2022 16:30:13 +0200 Subject: net: dsa: felix: update destinations of existing traps with ocelot-8021q Historically, the felix DSA driver has installed special traps such that PTP over L2 works with the ocelot-8021q tagging protocol; commit 0a6f17c6ae21 ("net: dsa: tag_ocelot_8021q: add support for PTP timestamping") has the details. Then the ocelot switch library also gained more comprehensive support for PTP traps through commit 96ca08c05838 ("net: mscc: ocelot: set up traps for PTP packets"). Right now, PTP over L2 works using ocelot-8021q via the traps it has set for itself, but nothing else does. Consolidating the two code blocks would make ocelot-8021q gain support for PTP over L4 and tc-flower traps, and at the same time avoid some code and TCAM duplication. The traps are similar in intent, but different in execution, so some explanation is required. The traps set up by felix_setup_mmio_filtering() are VCAP IS1 filters, which have a PAG that chains them to a VCAP IS2 filter, and the IS2 is where the 'trap' action resides. The traps set up by ocelot_trap_add(), on the other hand, have a single filter, in VCAP IS2. The reason for chaining VCAP IS1 and IS2 in Felix was to ensure that the hardcoded traps take precedence and cannot be overridden by the Ocelot switch library. So in principle, the PTP traps needed for ocelot-8021q in the Felix driver can rely on ocelot_trap_add(), but the filters need to be patched to account for a quirk that LS1028A has: the quirk_no_xtr_irq described in commit 0a6f17c6ae21 ("net: dsa: tag_ocelot_8021q: add support for PTP timestamping"). Live-patching is done by iterating through the trap list every time we know it has been updated, and transforming a trap into a redirect + CPU copy if ocelot-8021q is in use. Making the DSA ocelot-8021q tagger work with the Ocelot traps means we can eliminate the dedicated OCELOT_VCAP_IS1_TAG_8021Q_PTP_MMIO and OCELOT_VCAP_IS2_TAG_8021Q_PTP_MMIO cookies. To minimize the patch delta, OCELOT_VCAP_IS2_MRP_TRAP takes the place of OCELOT_VCAP_IS2_TAG_8021Q_PTP_MMIO (the alternative would have been to left-shift all cookie numbers by 1). Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/dsa/ocelot/felix.c | 187 +++++++++++++++++------------------------ include/soc/mscc/ocelot_vcap.h | 4 +- 2 files changed, 77 insertions(+), 114 deletions(-) diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 7293eff0e90a..9ffd5491bf2d 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -267,30 +267,26 @@ static void felix_8021q_cpu_port_deinit(struct ocelot *ocelot, int port) mutex_unlock(&ocelot->fwd_domain_lock); } -/* Set up a VCAP IS2 rule for delivering PTP frames to the CPU port module. - * If the quirk_no_xtr_irq is in place, then also copy those PTP frames to the - * tag_8021q CPU port. +/* On switches with no extraction IRQ wired, trapped packets need to be + * replicated over Ethernet as well, otherwise we'd get no notification of + * their arrival when using the ocelot-8021q tagging protocol. */ -static int felix_setup_mmio_filtering(struct felix *felix) +static int felix_update_trapping_destinations(struct dsa_switch *ds, + bool using_tag_8021q) { - unsigned long user_ports = dsa_user_ports(felix->ds); - struct ocelot_vcap_filter *redirect_rule; - struct ocelot_vcap_filter *tagging_rule; - struct ocelot *ocelot = &felix->ocelot; - struct dsa_switch *ds = felix->ds; + struct ocelot *ocelot = ds->priv; + struct felix *felix = ocelot_to_felix(ocelot); + struct ocelot_vcap_filter *trap; + enum ocelot_mask_mode mask_mode; + unsigned long port_mask; struct dsa_port *dp; - int cpu = -1, ret; - - tagging_rule = kzalloc(sizeof(struct ocelot_vcap_filter), GFP_KERNEL); - if (!tagging_rule) - return -ENOMEM; + bool cpu_copy_ena; + int cpu = -1, err; - redirect_rule = kzalloc(sizeof(struct ocelot_vcap_filter), GFP_KERNEL); - if (!redirect_rule) { - kfree(tagging_rule); - return -ENOMEM; - } + if (!felix->info->quirk_no_xtr_irq) + return 0; + /* Figure out the current CPU port */ dsa_switch_for_each_cpu_port(dp, ds) { cpu = dp->index; break; @@ -300,103 +296,46 @@ static int felix_setup_mmio_filtering(struct felix *felix) * dsa_tree_setup_default_cpu() would have failed earlier. */ - tagging_rule->key_type = OCELOT_VCAP_KEY_ETYPE; - *(__be16 *)tagging_rule->key.etype.etype.value = htons(ETH_P_1588); - *(__be16 *)tagging_rule->key.etype.etype.mask = htons(0xffff); - tagging_rule->ingress_port_mask = user_ports; - tagging_rule->prio = 1; - tagging_rule->id.cookie = OCELOT_VCAP_IS1_TAG_8021Q_PTP_MMIO(ocelot); - tagging_rule->id.tc_offload = false; - tagging_rule->block_id = VCAP_IS1; - tagging_rule->type = OCELOT_VCAP_FILTER_OFFLOAD; - tagging_rule->lookup = 0; - tagging_rule->action.pag_override_mask = 0xff; - tagging_rule->action.pag_val = ocelot->num_phys_ports; - - ret = ocelot_vcap_filter_add(ocelot, tagging_rule, NULL); - if (ret) { - kfree(tagging_rule); - kfree(redirect_rule); - return ret; - } + /* Make sure all traps are set up for that destination */ + list_for_each_entry(trap, &ocelot->traps, trap_list) { + /* Figure out the current trapping destination */ + if (using_tag_8021q) { + /* Redirect to the tag_8021q CPU port. If timestamps + * are necessary, also copy trapped packets to the CPU + * port module. + */ + mask_mode = OCELOT_MASK_MODE_REDIRECT; + port_mask = BIT(cpu); + cpu_copy_ena = !!trap->take_ts; + } else { + /* Trap packets only to the CPU port module, which is + * redirected to the NPI port (the DSA CPU port) + */ + mask_mode = OCELOT_MASK_MODE_PERMIT_DENY; + port_mask = 0; + cpu_copy_ena = true; + } - redirect_rule->key_type = OCELOT_VCAP_KEY_ANY; - redirect_rule->ingress_port_mask = user_ports; - redirect_rule->pag = ocelot->num_phys_ports; - redirect_rule->prio = 1; - redirect_rule->id.cookie = OCELOT_VCAP_IS2_TAG_8021Q_PTP_MMIO(ocelot); - redirect_rule->id.tc_offload = false; - redirect_rule->block_id = VCAP_IS2; - redirect_rule->type = OCELOT_VCAP_FILTER_OFFLOAD; - redirect_rule->lookup = 0; - redirect_rule->action.cpu_copy_ena = true; - if (felix->info->quirk_no_xtr_irq) { - /* Redirect to the tag_8021q CPU but also copy PTP packets to - * the CPU port module - */ - redirect_rule->action.mask_mode = OCELOT_MASK_MODE_REDIRECT; - redirect_rule->action.port_mask = BIT(cpu); - } else { - /* Trap PTP packets only to the CPU port module (which is - * redirected to the NPI port) - */ - redirect_rule->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY; - redirect_rule->action.port_mask = 0; - } + if (trap->action.mask_mode == mask_mode && + trap->action.port_mask == port_mask && + trap->action.cpu_copy_ena == cpu_copy_ena) + continue; - ret = ocelot_vcap_filter_add(ocelot, redirect_rule, NULL); - if (ret) { - ocelot_vcap_filter_del(ocelot, tagging_rule); - kfree(redirect_rule); - return ret; - } + trap->action.mask_mode = mask_mode; + trap->action.port_mask = port_mask; + trap->action.cpu_copy_ena = cpu_copy_ena; - /* The ownership of the CPU port module's queues might have just been - * transferred to the tag_8021q tagger from the NPI-based tagger. - * So there might still be all sorts of crap in the queues. On the - * other hand, the MMIO-based matching of PTP frames is very brittle, - * so we need to be careful that there are no extra frames to be - * dequeued over MMIO, since we would never know to discard them. - */ - ocelot_drain_cpu_queue(ocelot, 0); + err = ocelot_vcap_filter_replace(ocelot, trap); + if (err) + return err; + } return 0; } -static int felix_teardown_mmio_filtering(struct felix *felix) -{ - struct ocelot_vcap_filter *tagging_rule, *redirect_rule; - struct ocelot_vcap_block *block_vcap_is1; - struct ocelot_vcap_block *block_vcap_is2; - struct ocelot *ocelot = &felix->ocelot; - int err; - - block_vcap_is1 = &ocelot->block[VCAP_IS1]; - block_vcap_is2 = &ocelot->block[VCAP_IS2]; - - tagging_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_is1, - ocelot->num_phys_ports, - false); - if (!tagging_rule) - return -ENOENT; - - err = ocelot_vcap_filter_del(ocelot, tagging_rule); - if (err) - return err; - - redirect_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_is2, - ocelot->num_phys_ports, - false); - if (!redirect_rule) - return -ENOENT; - - return ocelot_vcap_filter_del(ocelot, redirect_rule); -} - static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu) { struct ocelot *ocelot = ds->priv; - struct felix *felix = ocelot_to_felix(ocelot); unsigned long cpu_flood; struct dsa_port *dp; int err; @@ -432,10 +371,19 @@ static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu) if (err) return err; - err = felix_setup_mmio_filtering(felix); + err = felix_update_trapping_destinations(ds, true); if (err) goto out_tag_8021q_unregister; + /* The ownership of the CPU port module's queues might have just been + * transferred to the tag_8021q tagger from the NPI-based tagger. + * So there might still be all sorts of crap in the queues. On the + * other hand, the MMIO-based matching of PTP frames is very brittle, + * so we need to be careful that there are no extra frames to be + * dequeued over MMIO, since we would never know to discard them. + */ + ocelot_drain_cpu_queue(ocelot, 0); + return 0; out_tag_8021q_unregister: @@ -446,11 +394,10 @@ out_tag_8021q_unregister: static void felix_teardown_tag_8021q(struct dsa_switch *ds, int cpu) { struct ocelot *ocelot = ds->priv; - struct felix *felix = ocelot_to_felix(ocelot); struct dsa_port *dp; int err; - err = felix_teardown_mmio_filtering(felix); + err = felix_update_trapping_destinations(ds, false); if (err) dev_err(ds->dev, "felix_teardown_mmio_filtering returned %d", err); @@ -1279,8 +1226,17 @@ static int felix_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr) { struct ocelot *ocelot = ds->priv; + struct felix *felix = ocelot_to_felix(ocelot); + bool using_tag_8021q; + int err; - return ocelot_hwstamp_set(ocelot, port, ifr); + err = ocelot_hwstamp_set(ocelot, port, ifr); + if (err) + return err; + + using_tag_8021q = felix->tag_proto == DSA_TAG_PROTO_OCELOT_8021Q; + + return felix_update_trapping_destinations(ds, using_tag_8021q); } static bool felix_check_xtr_pkt(struct ocelot *ocelot, unsigned int ptp_type) @@ -1407,8 +1363,17 @@ static int felix_cls_flower_add(struct dsa_switch *ds, int port, struct flow_cls_offload *cls, bool ingress) { struct ocelot *ocelot = ds->priv; + struct felix *felix = ocelot_to_felix(ocelot); + bool using_tag_8021q; + int err; + + err = ocelot_cls_flower_replace(ocelot, port, cls, ingress); + if (err) + return err; + + using_tag_8021q = felix->tag_proto == DSA_TAG_PROTO_OCELOT_8021Q; - return ocelot_cls_flower_replace(ocelot, port, cls, ingress); + return felix_update_trapping_destinations(ds, using_tag_8021q); } static int felix_cls_flower_del(struct dsa_switch *ds, int port, diff --git a/include/soc/mscc/ocelot_vcap.h b/include/soc/mscc/ocelot_vcap.h index 50af64e2ca3c..deb2ad9eb0a5 100644 --- a/include/soc/mscc/ocelot_vcap.h +++ b/include/soc/mscc/ocelot_vcap.h @@ -13,16 +13,14 @@ */ #define OCELOT_VCAP_ES0_TAG_8021Q_RXVLAN(ocelot, port) (port) #define OCELOT_VCAP_IS1_TAG_8021Q_TXVLAN(ocelot, port) (port) -#define OCELOT_VCAP_IS1_TAG_8021Q_PTP_MMIO(ocelot) ((ocelot)->num_phys_ports) #define OCELOT_VCAP_IS2_TAG_8021Q_TXVLAN(ocelot, port) (port) #define OCELOT_VCAP_IS2_MRP_REDIRECT(ocelot, port) ((ocelot)->num_phys_ports + (port)) -#define OCELOT_VCAP_IS2_TAG_8021Q_PTP_MMIO(ocelot) ((ocelot)->num_phys_ports * 2) +#define OCELOT_VCAP_IS2_MRP_TRAP(ocelot) ((ocelot)->num_phys_ports * 2) #define OCELOT_VCAP_IS2_L2_PTP_TRAP(ocelot) ((ocelot)->num_phys_ports * 2 + 1) #define OCELOT_VCAP_IS2_IPV4_GEN_PTP_TRAP(ocelot) ((ocelot)->num_phys_ports * 2 + 2) #define OCELOT_VCAP_IS2_IPV4_EV_PTP_TRAP(ocelot) ((ocelot)->num_phys_ports * 2 + 3) #define OCELOT_VCAP_IS2_IPV6_GEN_PTP_TRAP(ocelot) ((ocelot)->num_phys_ports * 2 + 4) #define OCELOT_VCAP_IS2_IPV6_EV_PTP_TRAP(ocelot) ((ocelot)->num_phys_ports * 2 + 5) -#define OCELOT_VCAP_IS2_MRP_TRAP(ocelot) ((ocelot)->num_phys_ports * 2 + 6) /* ================================================================= * VCAP Common -- cgit v1.2.3 From 29940ce32a2da2f3857d5fc459c6df6e47adb67e Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 16 Feb 2022 16:30:14 +0200 Subject: net: dsa: tag_ocelot_8021q: calculate TX checksum in software for deferred packets DSA inherits NETIF_F_CSUM_MASK from master->vlan_features, and the expectation is that TX checksumming is offloaded and not done in software. Normally the DSA master takes care of this, but packets handled by ocelot_defer_xmit() are a very special exception, because they are actually injected into the switch through register-based MMIO. So the DSA master is not involved at all for these packets => no one calculates the checksum. This allows PTP over UDP to work using the ocelot-8021q tagging protocol. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- net/dsa/tag_ocelot_8021q.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/net/dsa/tag_ocelot_8021q.c b/net/dsa/tag_ocelot_8021q.c index 68982b2789a5..bd6f1d0e5372 100644 --- a/net/dsa/tag_ocelot_8021q.c +++ b/net/dsa/tag_ocelot_8021q.c @@ -32,6 +32,13 @@ static struct sk_buff *ocelot_defer_xmit(struct dsa_port *dp, if (!xmit_work_fn || !xmit_worker) return NULL; + /* PTP over IP packets need UDP checksumming. We may have inherited + * NETIF_F_HW_CSUM from the DSA master, but these packets are not sent + * through the DSA master, so calculate the checksum here. + */ + if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) + return NULL; + xmit_work = kzalloc(sizeof(*xmit_work), GFP_ATOMIC); if (!xmit_work) return NULL; -- cgit v1.2.3