From: Vladimir Oltean <olteanv@gmail.com> Date: 2021-01-29 01:01:42
From: Vladimir Oltean <vladimir.oltean@nxp.com>
Changes in v8:
- Make tagging driver module reference counting work per DSA switch tree
instead of per CPU port, to be compatible with the tag protocol changing
through sysfs.
- Refactor ocelot_apply_bridge_fwd_mask and call it immediately after
the is_dsa_8021q_cpu variable changes, i.e. in
felix_8021q_cpu_port_init and felix_8021q_cpu_port_deinit.
- Take reference on tagging driver module in dsa_find_tagger_by_name.
- Replaced DSA_NOTIFIER_TAG_PROTO_SET and DSA_NOTIFIER_TAG_PROTO_DEL
with a single DSA_NOTIFIER_TAG_PROTO.
- Combined .set_tag_protocol and .del_tag_protocol into a single
.change_tag_protocol, and we're no longer calling those 2 functions at
probe and unbind time.
- Adapted Felix to .change_tag_protocol. Kept felix_set_tag_protocol and
felix_del_tag_protocol, but now calling them privately from
felix_setup and felix_teardown.
- Used -EPROTONOSUPPORT instead of -EOPNOTSUPP as return code.
- Dropped some review tags due to amount of changes.
Changes in v7:
- Keep a copy of the tagging protocol in the DSA switch tree (patch 7/11)
- Call {set,del}_tag_protocol for DSA links with the tag_ops of the DSA
tree and not of their own dp, since the latter is an invalid pointer
never set up by anybody.
- Wrap the calls done at probe and remove time into some helper
functions called dsa_switch_inform_initial_tag_proto and
dsa_switch_inform_tag_proto_gone. Call dsa_switch_inform_tag_proto_gone
more vigorously during the probe error path.
- Hold the rtnl_mutex in dsa_tree_change_tag_proto and change the calling
convention such that drivers now expect rtnl_mutex to be held.
- Drop the rtnl_lock surrounding dsa_8021q_setup in the felix driver,
since some callers of .{set,del}_tag_protocol now hold the rtnl_mutex
and we'd run into a deadlock if we took it. That's also why all
callers needed to be converted to hold the lock, since otherwise
dsa_8021q_setup would have no guarantees short of passing it a bool
rtnl_is_held variable.
Changes in v6:
- Removed redundant tree_index from dsa_notifier_tag_proto_info.
- Call .{set,del}_tag_protocol for the DSA links too.
- Check for ops::set_tag_protocol only once instead of in a loop.
- Check for ops::set_tag_protocol in dsa_switch_tag_proto_set too.
Changes in v5:
- Split patch series in half, removing PTP bits.
- Split previous monolithic patch "net: dsa: felix: add new VLAN-based
tagger" into 3 smaller patches.
- Updated the sysfs documentation
- Made the tagger_lock per DSA switch tree instead of per DSA switch
- Using dsa_tree_notify instead of dsa_broadcast.
Changes in v4:
- Support simultaneous compilation of tag_ocelot.c and
tag_ocelot_8021q.c.
- Support runtime switchover between the two taggers, by using
echo ocelot-8021q > /sys/class/net/eno2/dsa/tagging
- We are now actually performing cleanup instead of just probe-time
setup, which is required for supporting tagger switchover.
- Now draining the CPU queues by continuously reading QS_XTR_READ, same
as Ocelot, instead of one-time asserting QS_XTR_FLUSH, which actually
needed a sleep to be effective.
Changes in v3:
Use a per-port bool is_dsa_8021q_cpu instead of a single dsa_8021q_cpu
variable, to be compatible with future work where there may be
potentially multiple tag_8021q CPU ports in a LAG.
Changes in v2:
Posted the entire rework necessary for PTP support using tag_8021q.c.
Added a larger audience to the series.
The Felix switch inside LS1028A has an issue. It has a 2.5G CPU port,
and the external ports, in the majority of use cases, run at 1G. This
means that, when the CPU injects traffic into the switch, it is very
easy to run into congestion. This is not to say that it is impossible to
enter congestion even with all ports running at the same speed, just
that the default configuration is already very prone to that by design.
Normally, the way to deal with that is using Ethernet flow control
(PAUSE frames).
However, this functionality is not working today with the ENETC - Felix
switch pair. The hardware issue is undergoing documentation right now as
an erratum within NXP, but several customers have been requesting a
reasonable workaround for it.
In truth, the LS1028A has 2 internal port pairs. The lack of flow control
is an issue only when NPI mode (Node Processor Interface, aka the mode
where the "CPU port module", which carries DSA-style tagged packets, is
connected to a regular Ethernet port) is used, and NPI mode is supported
by Felix on a single port.
In past BSPs, we have had setups where both internal port pairs were
enabled. We were advertising the following setup:
"data port" "control port"
(2.5G) (1G)
eno2 eno3
^ ^
| |
| regular | DSA-tagged
| frames | frames
| |
v v
swp4 swp5
This works but is highly unpractical, due to NXP shifting the task of
designing a functional system (choosing which port to use, depending on
type of traffic required) up to the end user. The swpN interfaces would
have to be bridged with swp4, in order for the eno2 "data port" to have
access to the outside network. And the swpN interfaces would still be
capable of IP networking. So running a DHCP client would give us two IP
interfaces from the same subnet, one assigned to eno2, and the other to
swpN (0, 1, 2, 3).
Also, the dual port design doesn't scale. When attaching another DSA
switch to a Felix port, the end result is that the "data port" cannot
carry any meaningful data to the external world, since it lacks the DSA
tags required to traverse the sja1105 switches below. All that traffic
needs to go through the "control port".
So in newer BSPs there was a desire to simplify that setup, and only
have one internal port pair:
eno2 eno3
^
|
| DSA-tagged x disabled
| frames
|
v
swp4 swp5
However, this setup only exacerbates the issue of not having flow
control on the NPI port, since that is the only port now. Also, there
are use cases that still require the "data port", such as IEEE 802.1CB
(TSN stream identification doesn't work over an NPI port), source
MAC address learning over NPI, etc.
Again, there is a desire to keep the simplicity of the single internal
port setup, while regaining the benefits of having a dedicated data port
as well. And this series attempts to deliver just that.
So the NPI functionality is disabled conditionally. Its purpose was:
- To ensure individually addressable ports on TX. This can be replaced
by using some designated VLAN tags which are pushed by the DSA tagger
code, then removed by the switch (so they are invisible to the outside
world and to the user).
- To ensure source port identification on RX. Again, this can be
replaced by using some designated VLAN tags to encapsulate all RX
traffic (each VLAN uniquely identifies a source port). The DSA tagger
determines which port it was based on the VLAN number, then removes
that header.
- To deliver PTP timestamps. This cannot be obtained through VLAN
headers, so we need to take a step back and see how else we can do
that. The Microchip Ocelot-1 (VSC7514 MIPS) driver performs manual
injection/extraction from the CPU port module using register-based
MMIO, and not over Ethernet. We will need to do the same from DSA,
which makes this tagger a sort of hybrid between DSA and pure
switchdev.
Vladimir Oltean (11):
net: dsa: tag_8021q: add helpers to deduce whether a VLAN ID is RX or
TX VLAN
net: mscc: ocelot: export VCAP structures to include/soc/mscc
net: mscc: ocelot: store a namespaced VCAP filter ID
net: mscc: ocelot: reapply bridge forwarding mask on bonding
join/leave
net: mscc: ocelot: don't use NPI tag prefix for the CPU port module
net: dsa: document the existing switch tree notifiers and add a new
one
net: dsa: keep a copy of the tagging protocol in the DSA switch tree
net: dsa: allow changing the tag protocol via the "tagging" device
attribute
net: dsa: felix: convert to the new .change_tag_protocol DSA API
net: dsa: add a second tagger for Ocelot switches based on tag_8021q
net: dsa: felix: perform switch setup for tag_8021q
Documentation/ABI/testing/sysfs-class-net-dsa | 11 +-
MAINTAINERS | 1 +
drivers/net/dsa/ocelot/Kconfig | 2 +
drivers/net/dsa/ocelot/felix.c | 525 ++++++++++++++++--
drivers/net/dsa/ocelot/felix.h | 2 +
drivers/net/dsa/ocelot/felix_vsc9959.c | 1 +
drivers/net/dsa/ocelot/seville_vsc9953.c | 1 +
drivers/net/ethernet/mscc/ocelot.c | 120 ++--
drivers/net/ethernet/mscc/ocelot_flower.c | 7 +-
drivers/net/ethernet/mscc/ocelot_net.c | 1 +
drivers/net/ethernet/mscc/ocelot_vcap.c | 19 +-
drivers/net/ethernet/mscc/ocelot_vcap.h | 295 +---------
drivers/net/ethernet/mscc/ocelot_vsc7514.c | 2 -
include/linux/dsa/8021q.h | 14 +
include/net/dsa.h | 18 +-
include/soc/mscc/ocelot.h | 6 +-
include/soc/mscc/ocelot_vcap.h | 297 ++++++++++
net/dsa/Kconfig | 21 +-
net/dsa/Makefile | 1 +
net/dsa/dsa.c | 26 +
net/dsa/dsa2.c | 128 ++++-
net/dsa/dsa_priv.h | 17 +
net/dsa/master.c | 39 +-
net/dsa/port.c | 44 +-
net/dsa/slave.c | 35 +-
net/dsa/switch.c | 55 ++
net/dsa/tag_8021q.c | 15 +-
net/dsa/tag_ocelot_8021q.c | 68 +++
28 files changed, 1341 insertions(+), 430 deletions(-)
create mode 100644 net/dsa/tag_ocelot_8021q.c
--
2.25.1
From: Vladimir Oltean <olteanv@gmail.com> Date: 2021-01-29 01:01:42
From: Vladimir Oltean <vladimir.oltean@nxp.com>
The sja1105 implementation can be blind about this, but the felix driver
doesn't do exactly what it's being told, so it needs to know whether it
is a TX or an RX VLAN, so it can install the appropriate type of TCAM
rule.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
---
Changes in v8:
None.
Changes in v7:
None.
Changes in v6:
None.
Changes in v5:
None.
Changes in v4:
None.
Changes in v3:
None.
Changes in v2:
None.
include/linux/dsa/8021q.h | 14 ++++++++++++++
net/dsa/tag_8021q.c | 15 +++++++++++++--
2 files changed, 27 insertions(+), 2 deletions(-)
From: Vladimir Oltean <olteanv@gmail.com> Date: 2021-01-29 01:01:42
From: Vladimir Oltean <vladimir.oltean@nxp.com>
The Felix driver will need to preinstall some VCAP filters for its
tag_8021q implementation (outside of the tc-flower offload logic), so
these need to be exported to the common includes.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
---
changes in v8:
none.
changes in v7:
none.
changes in v6:
none.
Changes in v5:
None.
Changes in v3:
None.
Changes in v2:
Patch is new.
Changes in v4:
Use EXPORT_SYMBOL for ocelot_vcap_filter_add and ocelot_vcap_filter_del
so that building as module does not fail.
Changes in v3:
None.
Changes in v2:
Patch is new.
drivers/net/ethernet/mscc/ocelot_net.c | 1 +
drivers/net/ethernet/mscc/ocelot_vcap.c | 2 +
drivers/net/ethernet/mscc/ocelot_vcap.h | 293 +-----------------------
include/soc/mscc/ocelot_vcap.h | 289 +++++++++++++++++++++++
4 files changed, 294 insertions(+), 291 deletions(-)
@@ -7,300 +7,11 @@#define _MSCC_OCELOT_VCAP_H_#include"ocelot.h"-#include"ocelot_police.h"-#include<net/sch_generic.h>-#include<net/pkt_cls.h>+#include<soc/mscc/ocelot_vcap.h>+#include<net/flow_offload.h>#define OCELOT_POLICER_DISCARD 0x17f-structocelot_ipv4{-u8addr[4];-};--enumocelot_vcap_bit{-OCELOT_VCAP_BIT_ANY,-OCELOT_VCAP_BIT_0,-OCELOT_VCAP_BIT_1-};--structocelot_vcap_u8{-u8value[1];-u8mask[1];-};--structocelot_vcap_u16{-u8value[2];-u8mask[2];-};--structocelot_vcap_u24{-u8value[3];-u8mask[3];-};--structocelot_vcap_u32{-u8value[4];-u8mask[4];-};--structocelot_vcap_u40{-u8value[5];-u8mask[5];-};--structocelot_vcap_u48{-u8value[6];-u8mask[6];-};--structocelot_vcap_u64{-u8value[8];-u8mask[8];-};--structocelot_vcap_u128{-u8value[16];-u8mask[16];-};--structocelot_vcap_vid{-u16value;-u16mask;-};--structocelot_vcap_ipv4{-structocelot_ipv4value;-structocelot_ipv4mask;-};--structocelot_vcap_udp_tcp{-u16value;-u16mask;-};--structocelot_vcap_port{-u8value;-u8mask;-};--enumocelot_vcap_key_type{-OCELOT_VCAP_KEY_ANY,-OCELOT_VCAP_KEY_ETYPE,-OCELOT_VCAP_KEY_LLC,-OCELOT_VCAP_KEY_SNAP,-OCELOT_VCAP_KEY_ARP,-OCELOT_VCAP_KEY_IPV4,-OCELOT_VCAP_KEY_IPV6-};--structocelot_vcap_key_vlan{-structocelot_vcap_vidvid;/* VLAN ID (12 bit) */-structocelot_vcap_u8pcp;/* PCP (3 bit) */-enumocelot_vcap_bitdei;/* DEI */-enumocelot_vcap_bittagged;/* Tagged/untagged frame */-};--structocelot_vcap_key_etype{-structocelot_vcap_u48dmac;-structocelot_vcap_u48smac;-structocelot_vcap_u16etype;-structocelot_vcap_u16data;/* MAC data */-};--structocelot_vcap_key_llc{-structocelot_vcap_u48dmac;-structocelot_vcap_u48smac;--/* LLC header: DSAP at byte 0, SSAP at byte 1, Control at byte 2 */-structocelot_vcap_u32llc;-};--structocelot_vcap_key_snap{-structocelot_vcap_u48dmac;-structocelot_vcap_u48smac;--/* SNAP header: Organization Code at byte 0, Type at byte 3 */-structocelot_vcap_u40snap;-};--structocelot_vcap_key_arp{-structocelot_vcap_u48smac;-enumocelot_vcap_bitarp;/* Opcode ARP/RARP */-enumocelot_vcap_bitreq;/* Opcode request/reply */-enumocelot_vcap_bitunknown;/* Opcode unknown */-enumocelot_vcap_bitsmac_match;/* Sender MAC matches SMAC */-enumocelot_vcap_bitdmac_match;/* Target MAC matches DMAC */--/**< Protocol addr. length 4, hardware length 6 */-enumocelot_vcap_bitlength;--enumocelot_vcap_bitip;/* Protocol address type IP */-enumocelot_vcap_bitethernet;/* Hardware address type Ethernet */-structocelot_vcap_ipv4sip;/* Sender IP address */-structocelot_vcap_ipv4dip;/* Target IP address */-};--structocelot_vcap_key_ipv4{-enumocelot_vcap_bitttl;/* TTL zero */-enumocelot_vcap_bitfragment;/* Fragment */-enumocelot_vcap_bitoptions;/* Header options */-structocelot_vcap_u8ds;-structocelot_vcap_u8proto;/* Protocol */-structocelot_vcap_ipv4sip;/* Source IP address */-structocelot_vcap_ipv4dip;/* Destination IP address */-structocelot_vcap_u48data;/* Not UDP/TCP: IP data */-structocelot_vcap_udp_tcpsport;/* UDP/TCP: Source port */-structocelot_vcap_udp_tcpdport;/* UDP/TCP: Destination port */-enumocelot_vcap_bittcp_fin;-enumocelot_vcap_bittcp_syn;-enumocelot_vcap_bittcp_rst;-enumocelot_vcap_bittcp_psh;-enumocelot_vcap_bittcp_ack;-enumocelot_vcap_bittcp_urg;-enumocelot_vcap_bitsip_eq_dip;/* SIP equals DIP */-enumocelot_vcap_bitsport_eq_dport;/* SPORT equals DPORT */-enumocelot_vcap_bitseq_zero;/* TCP sequence number is zero */-};--structocelot_vcap_key_ipv6{-structocelot_vcap_u8proto;/* IPv6 protocol */-structocelot_vcap_u128sip;/* IPv6 source (byte 0-7 ignored) */-structocelot_vcap_u128dip;/* IPv6 destination (byte 0-7 ignored) */-enumocelot_vcap_bitttl;/* TTL zero */-structocelot_vcap_u8ds;-structocelot_vcap_u48data;/* Not UDP/TCP: IP data */-structocelot_vcap_udp_tcpsport;-structocelot_vcap_udp_tcpdport;-enumocelot_vcap_bittcp_fin;-enumocelot_vcap_bittcp_syn;-enumocelot_vcap_bittcp_rst;-enumocelot_vcap_bittcp_psh;-enumocelot_vcap_bittcp_ack;-enumocelot_vcap_bittcp_urg;-enumocelot_vcap_bitsip_eq_dip;/* SIP equals DIP */-enumocelot_vcap_bitsport_eq_dport;/* SPORT equals DPORT */-enumocelot_vcap_bitseq_zero;/* TCP sequence number is zero */-};--enumocelot_mask_mode{-OCELOT_MASK_MODE_NONE,-OCELOT_MASK_MODE_PERMIT_DENY,-OCELOT_MASK_MODE_POLICY,-OCELOT_MASK_MODE_REDIRECT,-};--enumocelot_es0_tag{-OCELOT_NO_ES0_TAG,-OCELOT_ES0_TAG,-OCELOT_FORCE_PORT_TAG,-OCELOT_FORCE_UNTAG,-};--enumocelot_tag_tpid_sel{-OCELOT_TAG_TPID_SEL_8021Q,-OCELOT_TAG_TPID_SEL_8021AD,-};--structocelot_vcap_action{-union{-/* VCAP ES0 */-struct{-enumocelot_es0_tagpush_outer_tag;-enumocelot_es0_tagpush_inner_tag;-enumocelot_tag_tpid_seltag_a_tpid_sel;-inttag_a_vid_sel;-inttag_a_pcp_sel;-u16vid_a_val;-u8pcp_a_val;-u8dei_a_val;-enumocelot_tag_tpid_seltag_b_tpid_sel;-inttag_b_vid_sel;-inttag_b_pcp_sel;-u16vid_b_val;-u8pcp_b_val;-u8dei_b_val;-};--/* VCAP IS1 */-struct{-boolvid_replace_ena;-u16vid;-boolvlan_pop_cnt_ena;-intvlan_pop_cnt;-boolpcp_dei_ena;-u8pcp;-u8dei;-boolqos_ena;-u8qos_val;-u8pag_override_mask;-u8pag_val;-};--/* VCAP IS2 */-struct{-boolcpu_copy_ena;-u8cpu_qu_num;-enumocelot_mask_modemask_mode;-unsignedlongport_mask;-boolpolice_ena;-structocelot_policerpol;-u32pol_ix;-};-};-};--structocelot_vcap_stats{-u64bytes;-u64pkts;-u64used;-};--enumocelot_vcap_filter_type{-OCELOT_VCAP_FILTER_DUMMY,-OCELOT_VCAP_FILTER_PAG,-OCELOT_VCAP_FILTER_OFFLOAD,-};--structocelot_vcap_filter{-structlist_headlist;--enumocelot_vcap_filter_typetype;-intblock_id;-intgoto_target;-intlookup;-u8pag;-u16prio;-u32id;--structocelot_vcap_actionaction;-structocelot_vcap_statsstats;-/* For VCAP IS1 and IS2 */-unsignedlongingress_port_mask;-/* For VCAP ES0 */-structocelot_vcap_portingress_port;-structocelot_vcap_portegress_port;--enumocelot_vcap_bitdmac_mc;-enumocelot_vcap_bitdmac_bc;-structocelot_vcap_key_vlanvlan;--enumocelot_vcap_key_typekey_type;-union{-/* OCELOT_VCAP_KEY_ANY: No specific fields */-structocelot_vcap_key_etypeetype;-structocelot_vcap_key_llcllc;-structocelot_vcap_key_snapsnap;-structocelot_vcap_key_arparp;-structocelot_vcap_key_ipv4ipv4;-structocelot_vcap_key_ipv6ipv6;-}key;-};--intocelot_vcap_filter_add(structocelot*ocelot,-structocelot_vcap_filter*rule,-structnetlink_ext_ack*extack);-intocelot_vcap_filter_del(structocelot*ocelot,-structocelot_vcap_filter*rule);intocelot_vcap_filter_stats_update(structocelot*ocelot,structocelot_vcap_filter*rule);structocelot_vcap_filter*
@@ -400,4 +400,293 @@ enum vcap_es0_action_field {VCAP_ES0_ACT_HIT_STICKY,};+structocelot_ipv4{+u8addr[4];+};++enumocelot_vcap_bit{+OCELOT_VCAP_BIT_ANY,+OCELOT_VCAP_BIT_0,+OCELOT_VCAP_BIT_1+};++structocelot_vcap_u8{+u8value[1];+u8mask[1];+};++structocelot_vcap_u16{+u8value[2];+u8mask[2];+};++structocelot_vcap_u24{+u8value[3];+u8mask[3];+};++structocelot_vcap_u32{+u8value[4];+u8mask[4];+};++structocelot_vcap_u40{+u8value[5];+u8mask[5];+};++structocelot_vcap_u48{+u8value[6];+u8mask[6];+};++structocelot_vcap_u64{+u8value[8];+u8mask[8];+};++structocelot_vcap_u128{+u8value[16];+u8mask[16];+};++structocelot_vcap_vid{+u16value;+u16mask;+};++structocelot_vcap_ipv4{+structocelot_ipv4value;+structocelot_ipv4mask;+};++structocelot_vcap_udp_tcp{+u16value;+u16mask;+};++structocelot_vcap_port{+u8value;+u8mask;+};++enumocelot_vcap_key_type{+OCELOT_VCAP_KEY_ANY,+OCELOT_VCAP_KEY_ETYPE,+OCELOT_VCAP_KEY_LLC,+OCELOT_VCAP_KEY_SNAP,+OCELOT_VCAP_KEY_ARP,+OCELOT_VCAP_KEY_IPV4,+OCELOT_VCAP_KEY_IPV6+};++structocelot_vcap_key_vlan{+structocelot_vcap_vidvid;/* VLAN ID (12 bit) */+structocelot_vcap_u8pcp;/* PCP (3 bit) */+enumocelot_vcap_bitdei;/* DEI */+enumocelot_vcap_bittagged;/* Tagged/untagged frame */+};++structocelot_vcap_key_etype{+structocelot_vcap_u48dmac;+structocelot_vcap_u48smac;+structocelot_vcap_u16etype;+structocelot_vcap_u16data;/* MAC data */+};++structocelot_vcap_key_llc{+structocelot_vcap_u48dmac;+structocelot_vcap_u48smac;++/* LLC header: DSAP at byte 0, SSAP at byte 1, Control at byte 2 */+structocelot_vcap_u32llc;+};++structocelot_vcap_key_snap{+structocelot_vcap_u48dmac;+structocelot_vcap_u48smac;++/* SNAP header: Organization Code at byte 0, Type at byte 3 */+structocelot_vcap_u40snap;+};++structocelot_vcap_key_arp{+structocelot_vcap_u48smac;+enumocelot_vcap_bitarp;/* Opcode ARP/RARP */+enumocelot_vcap_bitreq;/* Opcode request/reply */+enumocelot_vcap_bitunknown;/* Opcode unknown */+enumocelot_vcap_bitsmac_match;/* Sender MAC matches SMAC */+enumocelot_vcap_bitdmac_match;/* Target MAC matches DMAC */++/**< Protocol addr. length 4, hardware length 6 */+enumocelot_vcap_bitlength;++enumocelot_vcap_bitip;/* Protocol address type IP */+enumocelot_vcap_bitethernet;/* Hardware address type Ethernet */+structocelot_vcap_ipv4sip;/* Sender IP address */+structocelot_vcap_ipv4dip;/* Target IP address */+};++structocelot_vcap_key_ipv4{+enumocelot_vcap_bitttl;/* TTL zero */+enumocelot_vcap_bitfragment;/* Fragment */+enumocelot_vcap_bitoptions;/* Header options */+structocelot_vcap_u8ds;+structocelot_vcap_u8proto;/* Protocol */+structocelot_vcap_ipv4sip;/* Source IP address */+structocelot_vcap_ipv4dip;/* Destination IP address */+structocelot_vcap_u48data;/* Not UDP/TCP: IP data */+structocelot_vcap_udp_tcpsport;/* UDP/TCP: Source port */+structocelot_vcap_udp_tcpdport;/* UDP/TCP: Destination port */+enumocelot_vcap_bittcp_fin;+enumocelot_vcap_bittcp_syn;+enumocelot_vcap_bittcp_rst;+enumocelot_vcap_bittcp_psh;+enumocelot_vcap_bittcp_ack;+enumocelot_vcap_bittcp_urg;+enumocelot_vcap_bitsip_eq_dip;/* SIP equals DIP */+enumocelot_vcap_bitsport_eq_dport;/* SPORT equals DPORT */+enumocelot_vcap_bitseq_zero;/* TCP sequence number is zero */+};++structocelot_vcap_key_ipv6{+structocelot_vcap_u8proto;/* IPv6 protocol */+structocelot_vcap_u128sip;/* IPv6 source (byte 0-7 ignored) */+structocelot_vcap_u128dip;/* IPv6 destination (byte 0-7 ignored) */+enumocelot_vcap_bitttl;/* TTL zero */+structocelot_vcap_u8ds;+structocelot_vcap_u48data;/* Not UDP/TCP: IP data */+structocelot_vcap_udp_tcpsport;+structocelot_vcap_udp_tcpdport;+enumocelot_vcap_bittcp_fin;+enumocelot_vcap_bittcp_syn;+enumocelot_vcap_bittcp_rst;+enumocelot_vcap_bittcp_psh;+enumocelot_vcap_bittcp_ack;+enumocelot_vcap_bittcp_urg;+enumocelot_vcap_bitsip_eq_dip;/* SIP equals DIP */+enumocelot_vcap_bitsport_eq_dport;/* SPORT equals DPORT */+enumocelot_vcap_bitseq_zero;/* TCP sequence number is zero */+};++enumocelot_mask_mode{+OCELOT_MASK_MODE_NONE,+OCELOT_MASK_MODE_PERMIT_DENY,+OCELOT_MASK_MODE_POLICY,+OCELOT_MASK_MODE_REDIRECT,+};++enumocelot_es0_tag{+OCELOT_NO_ES0_TAG,+OCELOT_ES0_TAG,+OCELOT_FORCE_PORT_TAG,+OCELOT_FORCE_UNTAG,+};++enumocelot_tag_tpid_sel{+OCELOT_TAG_TPID_SEL_8021Q,+OCELOT_TAG_TPID_SEL_8021AD,+};++structocelot_vcap_action{+union{+/* VCAP ES0 */+struct{+enumocelot_es0_tagpush_outer_tag;+enumocelot_es0_tagpush_inner_tag;+enumocelot_tag_tpid_seltag_a_tpid_sel;+inttag_a_vid_sel;+inttag_a_pcp_sel;+u16vid_a_val;+u8pcp_a_val;+u8dei_a_val;+enumocelot_tag_tpid_seltag_b_tpid_sel;+inttag_b_vid_sel;+inttag_b_pcp_sel;+u16vid_b_val;+u8pcp_b_val;+u8dei_b_val;+};++/* VCAP IS1 */+struct{+boolvid_replace_ena;+u16vid;+boolvlan_pop_cnt_ena;+intvlan_pop_cnt;+boolpcp_dei_ena;+u8pcp;+u8dei;+boolqos_ena;+u8qos_val;+u8pag_override_mask;+u8pag_val;+};++/* VCAP IS2 */+struct{+boolcpu_copy_ena;+u8cpu_qu_num;+enumocelot_mask_modemask_mode;+unsignedlongport_mask;+boolpolice_ena;+structocelot_policerpol;+u32pol_ix;+};+};+};++structocelot_vcap_stats{+u64bytes;+u64pkts;+u64used;+};++enumocelot_vcap_filter_type{+OCELOT_VCAP_FILTER_DUMMY,+OCELOT_VCAP_FILTER_PAG,+OCELOT_VCAP_FILTER_OFFLOAD,+};++structocelot_vcap_filter{+structlist_headlist;++enumocelot_vcap_filter_typetype;+intblock_id;+intgoto_target;+intlookup;+u8pag;+u16prio;+u32id;++structocelot_vcap_actionaction;+structocelot_vcap_statsstats;+/* For VCAP IS1 and IS2 */+unsignedlongingress_port_mask;+/* For VCAP ES0 */+structocelot_vcap_portingress_port;+structocelot_vcap_portegress_port;++enumocelot_vcap_bitdmac_mc;+enumocelot_vcap_bitdmac_bc;+structocelot_vcap_key_vlanvlan;++enumocelot_vcap_key_typekey_type;+union{+/* OCELOT_VCAP_KEY_ANY: No specific fields */+structocelot_vcap_key_etypeetype;+structocelot_vcap_key_llcllc;+structocelot_vcap_key_snapsnap;+structocelot_vcap_key_arparp;+structocelot_vcap_key_ipv4ipv4;+structocelot_vcap_key_ipv6ipv6;+}key;+};++intocelot_vcap_filter_add(structocelot*ocelot,+structocelot_vcap_filter*rule,+structnetlink_ext_ack*extack);+intocelot_vcap_filter_del(structocelot*ocelot,+structocelot_vcap_filter*rule);+#endif /* _OCELOT_VCAP_H_ */
From: Vladimir Oltean <olteanv@gmail.com> Date: 2021-01-29 01:02:05
From: Vladimir Oltean <vladimir.oltean@nxp.com>
We will be adding some private VCAP filters that should not interfere in
any way with the filters added using tc-flower. So we need to allocate
some IDs which will not be used by tc.
Currently ocelot uses an u32 id derived from the flow cookie, which in
itself is an unsigned long. This is a problem in itself, since on 64 bit
systems, sizeof(unsigned long)=8, so the driver is already truncating
these.
Create a struct ocelot_vcap_id which contains the full unsigned long
cookie from tc, as well as a boolean that is supposed to namespace the
filters added by tc with the ones that aren't.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
---
Changes in v8:
None.
Changes in v7:
None.
Changes in v6:
None.
Changes in v5:
None.
Changes in v4:
None.
Changes in v3:
None.
Changes in v2:
Patch is new.
drivers/net/ethernet/mscc/ocelot_flower.c | 7 ++++---
drivers/net/ethernet/mscc/ocelot_vcap.c | 16 ++++++++++++----
drivers/net/ethernet/mscc/ocelot_vcap.h | 3 ++-
include/soc/mscc/ocelot_vcap.h | 7 ++++++-
4 files changed, 24 insertions(+), 9 deletions(-)
@@ -622,7 +622,8 @@ static int ocelot_flower_parse(struct ocelot *ocelot, int port, bool ingress,intret;filter->prio=f->common.prio;-filter->id=f->cookie;+filter->id.cookie=f->cookie;+filter->id.tc_offload=true;ret=ocelot_flower_parse_action(ocelot,port,ingress,f,filter);if(ret)
@@ -717,7 +718,7 @@ int ocelot_cls_flower_destroy(struct ocelot *ocelot, int port,block=&ocelot->block[block_id];-filter=ocelot_vcap_block_find_filter_by_id(block,f->cookie);+filter=ocelot_vcap_block_find_filter_by_id(block,f->cookie,true);if(!filter)return0;
@@ -741,7 +742,7 @@ int ocelot_cls_flower_stats(struct ocelot *ocelot, int port,block=&ocelot->block[block_id];-filter=ocelot_vcap_block_find_filter_by_id(block,f->cookie);+filter=ocelot_vcap_block_find_filter_by_id(block,f->cookie,true);if(!filter||filter->type==OCELOT_VCAP_FILTER_DUMMY)return0;
From: Vladimir Oltean <olteanv@gmail.com> Date: 2021-01-29 01:02:12
From: Vladimir Oltean <vladimir.oltean@nxp.com>
Applying the bridge forwarding mask currently is done only on the STP
state changes for any port. But it depends on both STP state changes,
and bonding interface state changes. Export the bit that recalculates
the forwarding mask so that it could be reused, and call it when a port
starts and stops offloading a bonding interface.
Now that the logic is split into a separate function, we can rename "p"
into "port", since the "port" variable was already taken in
ocelot_bridge_stp_state_set. Also, we can rename "i" into "lag", to make
it more clear what is it that we're iterating through.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
---
Changes in v8:
None.
Changes in v7:
None.
Changes in v6:
None.
Jakub, just FYI: ./scripts/get_maintainer.pl parses the "bpf" string
from the patchwork instance name, and wants me to CC the BPF maintainers
because of that.
Changes in v5:
None.
Changes in v4:
Patch is carried over from the "LAG offload for Ocelot DSA switches"
series:
https://patchwork.kernel.org/project/netdevbpf/patch/20210116005943.219479-10-olteanv@gmail.com/
I need it here because it refactors ocelot_apply_bridge_fwd_mask into a
separate function which I also need to call from felix now.
drivers/net/ethernet/mscc/ocelot.c | 63 +++++++++++++++++-------------
1 file changed, 36 insertions(+), 27 deletions(-)
@@ -889,10 +889,42 @@ int ocelot_get_ts_info(struct ocelot *ocelot, int port,}EXPORT_SYMBOL(ocelot_get_ts_info);+staticvoidocelot_apply_bridge_fwd_mask(structocelot*ocelot)+{+intport;++/* Apply FWD mask. The loop is needed to add/remove the current port as+*asourcefortheotherports.+*/+for(port=0;port<ocelot->num_phys_ports;port++){+if(ocelot->bridge_fwd_mask&BIT(port)){+unsignedlongmask=ocelot->bridge_fwd_mask&~BIT(port);+intlag;++for(lag=0;lag<ocelot->num_phys_ports;lag++){+unsignedlongbond_mask=ocelot->lags[lag];++if(!bond_mask)+continue;++if(bond_mask&BIT(port)){+mask&=~bond_mask;+break;+}+}++ocelot_write_rix(ocelot,mask,+ANA_PGID_PGID,PGID_SRC+port);+}else{+ocelot_write_rix(ocelot,0,+ANA_PGID_PGID,PGID_SRC+port);+}+}+}+voidocelot_bridge_stp_state_set(structocelot*ocelot,intport,u8state){u32port_cfg;-intp,i;if(!(BIT(port)&ocelot->bridge_mask))return;
@@ -915,32 +947,7 @@ void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state)ocelot_write_gix(ocelot,port_cfg,ANA_PORT_PORT_CFG,port);-/* Apply FWD mask. The loop is needed to add/remove the current port as-*asourcefortheotherports.-*/-for(p=0;p<ocelot->num_phys_ports;p++){-if(ocelot->bridge_fwd_mask&BIT(p)){-unsignedlongmask=ocelot->bridge_fwd_mask&~BIT(p);--for(i=0;i<ocelot->num_phys_ports;i++){-unsignedlongbond_mask=ocelot->lags[i];--if(!bond_mask)-continue;--if(bond_mask&BIT(p)){-mask&=~bond_mask;-break;-}-}--ocelot_write_rix(ocelot,mask,-ANA_PGID_PGID,PGID_SRC+p);-}else{-ocelot_write_rix(ocelot,0,-ANA_PGID_PGID,PGID_SRC+p);-}-}+ocelot_apply_bridge_fwd_mask(ocelot);}EXPORT_SYMBOL(ocelot_bridge_stp_state_set);
@@ -1297,6 +1304,7 @@ int ocelot_port_lag_join(struct ocelot *ocelot, int port,}ocelot_setup_lag(ocelot,lag);+ocelot_apply_bridge_fwd_mask(ocelot);ocelot_set_aggr_pgids(ocelot);return0;
@@ -1330,6 +1338,7 @@ void ocelot_port_lag_leave(struct ocelot *ocelot, int port,ocelot_write_gix(ocelot,port_cfg|ANA_PORT_PORT_CFG_PORTID_VAL(port),ANA_PORT_PORT_CFG,port);+ocelot_apply_bridge_fwd_mask(ocelot);ocelot_set_aggr_pgids(ocelot);}EXPORT_SYMBOL(ocelot_port_lag_leave);
From: Vladimir Oltean <olteanv@gmail.com> Date: 2021-01-29 01:02:34
From: Vladimir Oltean <vladimir.oltean@nxp.com>
Context: Ocelot switches put the injection/extraction frame header in
front of the Ethernet header. When used in NPI mode, a DSA master would
see junk instead of the destination MAC address, and it would most
likely drop the packets. So the Ocelot frame header can have an optional
prefix, which is just "ff:ff:ff:ff:ff:fe > ff:ff:ff:ff:ff:ff" padding
put before the actual tag (still before the real Ethernet header) such
that the DSA master thinks it's looking at a broadcast frame with a
strange EtherType.
Unfortunately, a lesson learned in commit 69df578c5f4b ("net: mscc:
ocelot: eliminate confusion between CPU and NPI port") seems to have
been forgotten in the meanwhile.
The CPU port module and the NPI port have independent settings for the
length of the tag prefix. However, the driver is using the same variable
to program both of them.
There is no reason really to use any tag prefix with the CPU port
module, since that is not connected to any Ethernet port. So this patch
makes the inj_prefix and xtr_prefix variables apply only to the NPI
port (which the switchdev ocelot_vsc7514 driver does not use).
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
---
Changes in v8:
None.
Changes in v7:
None.
Changes in v6:
None.
Changes in v5:
None.
Changes in v4:
Patch is new.
drivers/net/dsa/ocelot/felix.c | 8 ++++----
drivers/net/ethernet/mscc/ocelot.c | 12 ++++++------
drivers/net/ethernet/mscc/ocelot_vsc7514.c | 2 --
include/soc/mscc/ocelot.h | 4 ++--
4 files changed, 12 insertions(+), 14 deletions(-)
@@ -425,8 +425,8 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)ocelot->num_mact_rows=felix->info->num_mact_rows;ocelot->vcap=felix->info->vcap;ocelot->ops=felix->info->ops;-ocelot->inj_prefix=OCELOT_TAG_PREFIX_SHORT;-ocelot->xtr_prefix=OCELOT_TAG_PREFIX_SHORT;+ocelot->npi_inj_prefix=OCELOT_TAG_PREFIX_SHORT;+ocelot->npi_xtr_prefix=OCELOT_TAG_PREFIX_SHORT;ocelot->devlink=felix->ds->devlink;port_phy_modes=kcalloc(num_phys_ports,sizeof(phy_interface_t),
@@ -541,9 +541,9 @@ static void felix_npi_port_init(struct ocelot *ocelot, int port)/* NPI port Injection/Extraction configuration */ocelot_fields_write(ocelot,port,SYS_PORT_MODE_INCL_XTR_HDR,-ocelot->xtr_prefix);+ocelot->npi_xtr_prefix);ocelot_fields_write(ocelot,port,SYS_PORT_MODE_INCL_INJ_HDR,-ocelot->inj_prefix);+ocelot->npi_inj_prefix);/* Disable transmission of pause frames */ocelot_fields_write(ocelot,port,SYS_PAUSE_CFG_PAUSE_ENA,0);
@@ -1391,9 +1391,9 @@ int ocelot_get_max_mtu(struct ocelot *ocelot, int port)if(port==ocelot->npi){max_mtu-=OCELOT_TAG_LEN;-if(ocelot->inj_prefix==OCELOT_TAG_PREFIX_SHORT)+if(ocelot->npi_inj_prefix==OCELOT_TAG_PREFIX_SHORT)max_mtu-=OCELOT_SHORT_PREFIX_LEN;-elseif(ocelot->inj_prefix==OCELOT_TAG_PREFIX_LONG)+elseif(ocelot->npi_inj_prefix==OCELOT_TAG_PREFIX_LONG)max_mtu-=OCELOT_LONG_PREFIX_LEN;}
@@ -1478,9 +1478,9 @@ static void ocelot_cpu_port_init(struct ocelot *ocelot)ocelot_fields_write(ocelot,cpu,QSYS_SWITCH_PORT_MODE_PORT_ENA,1);/* CPU port Injection/Extraction configuration */ocelot_fields_write(ocelot,cpu,SYS_PORT_MODE_INCL_XTR_HDR,-ocelot->xtr_prefix);+OCELOT_TAG_PREFIX_NONE);ocelot_fields_write(ocelot,cpu,SYS_PORT_MODE_INCL_INJ_HDR,-ocelot->inj_prefix);+OCELOT_TAG_PREFIX_NONE);/* Configure the CPU port to be VLAN aware */ocelot_write_gix(ocelot,ANA_PORT_VLAN_CFG_VLAN_VID(0)|
From: Vladimir Oltean <olteanv@gmail.com> Date: 2021-01-29 01:03:15
From: Vladimir Oltean <vladimir.oltean@nxp.com>
Cascading DSA switches can be done multiple ways. There is the brute
force approach / tag stacking, where one upstream switch, located
between leaf switches and the host Ethernet controller, will just
happily transport the DSA header of those leaf switches as payload.
For this kind of setups, DSA works without any special kind of treatment
compared to a single switch - they just aren't aware of each other.
Then there's the approach where the upstream switch understands the tags
it transports from its leaves below, as it doesn't push a tag of its own,
but it routes based on the source port & switch id information present
in that tag (as opposed to DMAC & VID) and it strips the tag when
egressing a front-facing port. Currently only Marvell implements the
latter, and Marvell DSA trees contain only Marvell switches.
So it is safe to say that DSA trees already have a single tag protocol
shared by all switches, and in fact this is what makes the switches able
to understand each other. This fact is also implied by the fact that
currently, the tagging protocol is reported as part of a sysfs installed
on the DSA master and not per port, so it must be the same for all the
ports connected to that DSA master regardless of the switch that they
belong to.
It's time to make this official and enforce it (yes, this also means we
won't have any "switch understands tag to some extent but is not able to
speak it" hardware oddities that we'll support in the future).
This is needed due to the imminent introduction of the dsa_switch_ops::
change_tag_protocol driver API. When that is introduced, we'll have
to notify switches of the tagging protocol that they're configured to
use. Currently the tag_ops structure pointer is held only for CPU ports.
But there are switches which don't have CPU ports and nonetheless still
need to be configured. These would be Marvell leaf switches whose
upstream port is just a DSA link. How do we inform these of their
tagging protocol setup/deletion?
One answer to the above would be: iterate through the DSA switch tree's
ports once, list the CPU ports, get their tag_ops, then iterate again
now that we have it, and notify everybody of that tag_ops. But what to
do if conflicts appear between one cpu_dp->tag_ops and another? There's
no escaping the fact that conflict resolution needs to be done, so we
can be upfront about it.
Ease our work and just keep the master copy of the tag_ops inside the
struct dsa_switch_tree. Reference counting is now moved to be per-tree
too, instead of per-CPU port.
There are many places in the data path that access master->dsa_ptr->tag_ops
and we would introduce unnecessary performance penalty going through yet
another indirection, so keep those right where they are.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Changes in v8:
Make tagging driver module reference counting work per DSA switch tree
instead of per CPU port, to be compatible with the tag protocol changing
through sysfs.
Changes in v7:
Patch is new.
include/net/dsa.h | 7 ++++++-
net/dsa/dsa2.c | 36 ++++++++++++++++++++++++------------
2 files changed, 30 insertions(+), 13 deletions(-)
@@ -140,6 +140,9 @@ struct dsa_switch_tree {/* Has this tree been applied to the hardware? */boolsetup;+/* Tagging protocol operations */+conststructdsa_device_ops*tag_ops;+/**Configurationdatafortheplatformdevicethatowns*thisdsaswitchtreeinstance.
@@ -225,7 +228,9 @@ struct dsa_port {structnet_device*slave;};-/* CPU port tagging operations used by master or slave devices */+/* Copy of the tagging protocol operations, for quicker access+*inthedatapath.ValidonlyfortheCPUports.+*/conststructdsa_device_ops*tag_ops;/* Copies for faster access in master receive hot path */
@@ -1011,24 +1012,35 @@ static int dsa_port_parse_cpu(struct dsa_port *dp, struct net_device *master){structdsa_switch*ds=dp->ds;structdsa_switch_tree*dst=ds->dst;-conststructdsa_device_ops*tag_ops;enumdsa_tag_protocoltag_protocol;tag_protocol=dsa_get_tag_protocol(dp,master);-tag_ops=dsa_tag_driver_get(tag_protocol);-if(IS_ERR(tag_ops)){-if(PTR_ERR(tag_ops)==-ENOPROTOOPT)-return-EPROBE_DEFER;-dev_warn(ds->dev,"No tagger for this switch\n");-dp->master=NULL;-returnPTR_ERR(tag_ops);+if(dst->tag_ops){+if(dst->tag_ops->proto!=tag_protocol){+dev_err(ds->dev,+"A DSA switch tree can have only one tagging protocol\n");+return-EINVAL;+}+/* In the case of multiple CPU ports per switch, the tagging+*protocolisstillreference-countedonlyperswitchtree,so+*nothingtodohere.+*/+}else{+dst->tag_ops=dsa_tag_driver_get(tag_protocol);+if(IS_ERR(dst->tag_ops)){+if(PTR_ERR(dst->tag_ops)==-ENOPROTOOPT)+return-EPROBE_DEFER;+dev_warn(ds->dev,"No tagger for this switch\n");+dp->master=NULL;+returnPTR_ERR(dst->tag_ops);+}}dp->master=master;dp->type=DSA_PORT_TYPE_CPU;-dp->filter=tag_ops->filter;-dp->rcv=tag_ops->rcv;-dp->tag_ops=tag_ops;+dp->filter=dst->tag_ops->filter;+dp->rcv=dst->tag_ops->rcv;+dp->tag_ops=dst->tag_ops;dp->dst=dst;return0;
From: Vladimir Oltean <olteanv@gmail.com> Date: 2021-01-29 01:03:21
From: Vladimir Oltean <vladimir.oltean@nxp.com>
The existence of dsa_broadcast has generated some confusion in the past:
https://www.mail-archive.com/netdev@vger.kernel.org/msg365042.html
So let's document the existing dsa_port_notify and dsa_broadcast
functions and explain when each of them should be used.
Also, in fact, the in-between function has always been there but was
lacking a name, and is the main reason for this patch: dsa_tree_notify.
Refactor dsa_broadcast to use it.
This patch also moves dsa_broadcast (a top-level function) to dsa2.c,
where it really belonged in the first place, but had no companion so it
stood with dsa_port_notify.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
---
Changes in v8:
None.
Changes in v7:
None.
Changes in v6:
None.
Changes in v5:
Patch is new.
net/dsa/dsa2.c | 43 +++++++++++++++++++++++++++++++++++++++++++
net/dsa/dsa_priv.h | 2 ++
net/dsa/port.c | 36 +++++++++++++-----------------------
3 files changed, 58 insertions(+), 23 deletions(-)
From: Vladimir Oltean <olteanv@gmail.com> Date: 2021-01-29 01:03:35
From: Vladimir Oltean <vladimir.oltean@nxp.com>
Currently DSA exposes the following sysfs:
$ cat /sys/class/net/eno2/dsa/tagging
ocelot
which is a read-only device attribute, introduced in the kernel as
commit 98cdb4807123 ("net: dsa: Expose tagging protocol to user-space"),
and used by libpcap since its commit 993db3800d7d ("Add support for DSA
link-layer types").
It would be nice if we could extend this device attribute by making it
writable:
$ echo ocelot-8021q > /sys/class/net/eno2/dsa/tagging
This is useful with DSA switches that can make use of more than one
tagging protocol. It may be useful in dsa_loop in the future too, to
perform offline testing of various taggers, or for changing between dsa
and edsa on Marvell switches, if that is desirable.
In terms of implementation, drivers can support this feature by
implementing .change_tag_protocol, which should always leave the switch
in a consistent state: either with the new protocol if things went well,
or with the old one if something failed. Teardown of the old protocol,
if necessary, must be handled by the driver.
Some things remain as before:
- The .get_tag_protocol is currently only called at probe time, to load
the initial tagging protocol driver. Nonetheless, new drivers should
report the tagging protocol in current use now.
- The driver should manage by itself the initial setup of tagging
protocol, no later than the .setup() method, as well as destroying
resources used by the last tagger in use, no earlier than the
.teardown() method.
For multi-switch DSA trees, error handling is a bit more complicated,
since e.g. the 5th out of 7 switches may fail to change the tag
protocol. When that happens, a revert to the original tag protocol is
attempted, but that may fail too, leaving the tree in an inconsistent
state despite each individual switch implementing .change_tag_protocol
transactionally. Since the intersection between drivers that implement
.change_tag_protocol and drivers that support D in DSA is currently the
empty set, the possibility for this error to happen is ignored for now.
Testing:
$ insmod mscc_felix.ko
[ 79.549784] mscc_felix 0000:00:00.5: Adding to iommu group 14
[ 79.565712] mscc_felix 0000:00:00.5: Failed to register DSA switch: -517
$ insmod tag_ocelot.ko
$ rmmod mscc_felix.ko
$ insmod mscc_felix.ko
[ 97.261724] libphy: VSC9959 internal MDIO bus: probed
[ 97.267363] mscc_felix 0000:00:00.5: Found PCS at internal MDIO address 0
[ 97.274998] mscc_felix 0000:00:00.5: Found PCS at internal MDIO address 1
[ 97.282561] mscc_felix 0000:00:00.5: Found PCS at internal MDIO address 2
[ 97.289700] mscc_felix 0000:00:00.5: Found PCS at internal MDIO address 3
[ 97.599163] mscc_felix 0000:00:00.5 swp0 (uninitialized): PHY [0000:00:00.3:10] driver [Microsemi GE VSC8514 SyncE] (irq=POLL)
[ 97.862034] mscc_felix 0000:00:00.5 swp1 (uninitialized): PHY [0000:00:00.3:11] driver [Microsemi GE VSC8514 SyncE] (irq=POLL)
[ 97.950731] mscc_felix 0000:00:00.5 swp0: configuring for inband/qsgmii link mode
[ 97.964278] 8021q: adding VLAN 0 to HW filter on device swp0
[ 98.146161] mscc_felix 0000:00:00.5 swp2 (uninitialized): PHY [0000:00:00.3:12] driver [Microsemi GE VSC8514 SyncE] (irq=POLL)
[ 98.238649] mscc_felix 0000:00:00.5 swp1: configuring for inband/qsgmii link mode
[ 98.251845] 8021q: adding VLAN 0 to HW filter on device swp1
[ 98.433916] mscc_felix 0000:00:00.5 swp3 (uninitialized): PHY [0000:00:00.3:13] driver [Microsemi GE VSC8514 SyncE] (irq=POLL)
[ 98.485542] mscc_felix 0000:00:00.5: configuring for fixed/internal link mode
[ 98.503584] mscc_felix 0000:00:00.5: Link is Up - 2.5Gbps/Full - flow control rx/tx
[ 98.527948] device eno2 entered promiscuous mode
[ 98.544755] DSA: tree 0 setup
$ ping 10.0.0.1
PING 10.0.0.1 (10.0.0.1): 56 data bytes
64 bytes from 10.0.0.1: seq=0 ttl=64 time=2.337 ms
64 bytes from 10.0.0.1: seq=1 ttl=64 time=0.754 ms
^C
--- 10.0.0.1 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.754/1.545/2.337 ms
$ cat /sys/class/net/eno2/dsa/tagging
ocelot
$ cat ./test_ocelot_8021q.sh
#!/bin/bash
ip link set swp0 down
ip link set swp1 down
ip link set swp2 down
ip link set swp3 down
ip link set swp5 down
ip link set eno2 down
echo ocelot-8021q > /sys/class/net/eno2/dsa/tagging
ip link set eno2 up
ip link set swp0 up
ip link set swp1 up
ip link set swp2 up
ip link set swp3 up
ip link set swp5 up
$ ./test_ocelot_8021q.sh
./test_ocelot_8021q.sh: line 9: echo: write error: Protocol not available
$ rmmod tag_ocelot.ko
rmmod: can't unload module 'tag_ocelot': Resource temporarily unavailable
$ insmod tag_ocelot_8021q.ko
$ ./test_ocelot_8021q.sh
$ cat /sys/class/net/eno2/dsa/tagging
ocelot-8021q
$ rmmod tag_ocelot.ko
$ rmmod tag_ocelot_8021q.ko
rmmod: can't unload module 'tag_ocelot_8021q': Resource temporarily unavailable
$ ping 10.0.0.1
PING 10.0.0.1 (10.0.0.1): 56 data bytes
64 bytes from 10.0.0.1: seq=0 ttl=64 time=0.953 ms
64 bytes from 10.0.0.1: seq=1 ttl=64 time=0.787 ms
64 bytes from 10.0.0.1: seq=2 ttl=64 time=0.771 ms
$ rmmod mscc_felix.ko
[ 645.544426] mscc_felix 0000:00:00.5: Link is Down
[ 645.838608] DSA: tree 0 torn down
$ rmmod tag_ocelot_8021q.ko
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Changes in v8:
- Take reference on tagging driver module in dsa_find_tagger_by_name.
- Replaced DSA_NOTIFIER_TAG_PROTO_SET and DSA_NOTIFIER_TAG_PROTO_DEL
with a single DSA_NOTIFIER_TAG_PROTO.
- Combined .set_tag_protocol and .del_tag_protocol into a single
.change_tag_protocol, and we're no longer calling those 2 functions at
probe and unbind time.
- Dropped review tags due to amount of changes.
Changes in v7:
- Hold the rtnl_mutex in dsa_tree_change_tag_proto and change the calling
convention such that drivers now expect rtnl_mutex to be held.
- Call {set,del}_tag_protocol for DSA links with the tag_ops of the DSA
tree and not of their own dp, since the latter is an invalid pointer
never set up by anybody.
- Wrap the calls done at probe and remove time into some helper
functions called dsa_switch_inform_initial_tag_proto and
dsa_switch_inform_tag_proto_gone. Call dsa_switch_inform_tag_proto_gone
more vigorously during the probe error path.
Changes in v6:
- Removed redundant tree_index from dsa_notifier_tag_proto_info.
- Call .{set,del}_tag_protocol for the DSA links too.
- Check for ops::set_tag_protocol only once instead of in a loop.
- Check for ops::set_tag_protocol in dsa_switch_tag_proto_set too.
Changes in v5:
- Update the sysfs documentation
- Make the tagger_lock per DSA switch tree instead of per DSA switch,
and hold it across the entire delete -> set procedure.
- Use dsa_tree_notify instead of dsa_broadcast.
Changes in v4:
Patch is new.
Documentation/ABI/testing/sysfs-class-net-dsa | 11 +++-
include/net/dsa.h | 9 +++
net/dsa/dsa.c | 26 +++++++++
net/dsa/dsa2.c | 55 ++++++++++++++++++-
net/dsa/dsa_priv.h | 15 +++++
net/dsa/master.c | 39 ++++++++++++-
net/dsa/port.c | 8 +++
net/dsa/slave.c | 35 ++++++++----
net/dsa/switch.c | 55 +++++++++++++++++++
9 files changed, 235 insertions(+), 18 deletions(-)
@@ -3,5 +3,12 @@ Date: August 2018 KernelVersion: 4.20 Contact: netdev@vger.kernel.org Description:- String indicating the type of tagging protocol used by the- DSA slave network device.+ On read, this file returns a string indicating the type of+ tagging protocol used by the DSA network devices that are+ attached to this master interface.+ On write, this file changes the tagging protocol of the+ attached DSA switches, if this operation is supported by the+ driver. Changing the tagging protocol must be done with the DSA+ interfaces and the master interface all administratively down.+ See the "name" field of each registered struct dsa_device_ops+ for a list of valid values.
@@ -84,6 +84,32 @@ const char *dsa_tag_protocol_to_str(const struct dsa_device_ops *ops)returnops->name;};+/* Function takes a reference on the module owning the tagger,+*sodsa_tag_driver_putmustbecalledafterwards.+*/+conststructdsa_device_ops*dsa_find_tagger_by_name(constchar*buf)+{+conststructdsa_device_ops*ops=ERR_PTR(-ENOPROTOOPT);+structdsa_tag_driver*dsa_tag_driver;++mutex_lock(&dsa_tag_drivers_lock);+list_for_each_entry(dsa_tag_driver,&dsa_tag_drivers_list,list){+conststructdsa_device_ops*tmp=dsa_tag_driver->ops;++if(!sysfs_streq(buf,tmp->name))+continue;++if(!try_module_get(dsa_tag_driver->owner))+break;++ops=tmp;+break;+}+mutex_unlock(&dsa_tag_drivers_lock);++returnops;+}+conststructdsa_device_ops*dsa_tag_driver_get(inttag_protocol){structdsa_tag_driver*dsa_tag_driver;
@@ -942,6 +942,57 @@ static void dsa_tree_teardown(struct dsa_switch_tree *dst)dst->setup=false;}+/* Since the dsa/tagging sysfs device attribute is per master, the assumption+*isthatallDSAswitcheswithinatreesharethesametagger,otherwise+*theywouldhaveformeddisjointtrees(different"dsa,member"values).+*/+intdsa_tree_change_tag_proto(structdsa_switch_tree*dst,+structnet_device*master,+conststructdsa_device_ops*tag_ops,+conststructdsa_device_ops*old_tag_ops)+{+structdsa_notifier_tag_proto_infoinfo;+structdsa_port*dp;+interr=-EBUSY;++if(!rtnl_trylock())+returnrestart_syscall();++/* At the moment we don't allow changing the tag protocol under+*traffic.Thertnl_mutexalsohappenstoserializeconcurrent+*attemptstochangethetaggingprotocol.IfweeverlifttheIFF_UP+*restriction,thereneedstobeanothermutexwhichserializesthis.+*/+if(master->flags&IFF_UP)+gotoout_unlock;++list_for_each_entry(dp,&dst->ports,list){+if(!dsa_is_user_port(dp->ds,dp->index))+continue;++if(dp->slave->flags&IFF_UP)+gotoout_unlock;+}++info.tag_ops=tag_ops;+err=dsa_tree_notify(dst,DSA_NOTIFIER_TAG_PROTO,&info);+if(err)+gotoout_unwind_tagger;++dst->tag_ops=tag_ops;++rtnl_unlock();++return0;++out_unwind_tagger:+info.tag_ops=old_tag_ops;+dsa_tree_notify(dst,DSA_NOTIFIER_TAG_PROTO,&info);+out_unlock:+rtnl_unlock();+returnerr;+}+staticstructdsa_port*dsa_port_touch(structdsa_switch*ds,intindex){structdsa_switch_tree*dst=ds->dst;
@@ -280,7 +280,44 @@ static ssize_t tagging_show(struct device *d, struct device_attribute *attr,returnsprintf(buf,"%s\n",dsa_tag_protocol_to_str(cpu_dp->tag_ops));}-staticDEVICE_ATTR_RO(tagging);++staticssize_ttagging_store(structdevice*d,structdevice_attribute*attr,+constchar*buf,size_tcount)+{+conststructdsa_device_ops*new_tag_ops,*old_tag_ops;+structnet_device*dev=to_net_dev(d);+structdsa_port*cpu_dp=dev->dsa_ptr;+interr;++old_tag_ops=cpu_dp->tag_ops;+new_tag_ops=dsa_find_tagger_by_name(buf);+/* Bad tagger name, or module is not loaded? */+if(IS_ERR(new_tag_ops))+returnPTR_ERR(new_tag_ops);++if(new_tag_ops==old_tag_ops)+/* Drop the temporarily held duplicate reference, since+*theDSAswitchtreeusesthistagger.+*/+gotoout;++err=dsa_tree_change_tag_proto(cpu_dp->ds->dst,dev,new_tag_ops,+old_tag_ops);+if(err){+/* On failure the old tagger is restored, so we don't need the+*driverforthenewone.+*/+dsa_tag_driver_put(new_tag_ops);+returnerr;+}++/* On success we no longer need the module for the old tagging protocol+*/+out:+dsa_tag_driver_put(old_tag_ops);+returncount;+}+staticDEVICE_ATTR_RW(tagging);staticstructattribute*dsa_slave_attrs[]={&dev_attr_tagging.attr,
@@ -1708,6 +1708,27 @@ static int dsa_slave_phy_setup(struct net_device *slave_dev)returnret;}+voiddsa_slave_setup_tagger(structnet_device*slave)+{+structdsa_port*dp=dsa_slave_to_port(slave);+structdsa_slave_priv*p=netdev_priv(slave);+conststructdsa_port*cpu_dp=dp->cpu_dp;+structnet_device*master=cpu_dp->master;++if(cpu_dp->tag_ops->tail_tag)+slave->needed_tailroom=cpu_dp->tag_ops->overhead;+else+slave->needed_headroom=cpu_dp->tag_ops->overhead;+/* Try to save one extra realloc later in the TX path (in the master)+*byalsoinheritingthemaster'sneededheadroomandtailroom.+*The8021qdriveralsodoesthis.+*/+slave->needed_headroom+=master->needed_headroom;+slave->needed_tailroom+=master->needed_tailroom;++p->xmit=cpu_dp->tag_ops->xmit;+}+staticstructlock_class_keydsa_slave_netdev_xmit_lock_key;staticvoiddsa_slave_set_lockdep_class_one(structnet_device*dev,structnetdev_queue*txq,
@@ -1782,16 +1803,6 @@ int dsa_slave_create(struct dsa_port *port)slave_dev->netdev_ops=&dsa_slave_netdev_ops;if(ds->ops->port_max_mtu)slave_dev->max_mtu=ds->ops->port_max_mtu(ds,port->index);-if(cpu_dp->tag_ops->tail_tag)-slave_dev->needed_tailroom=cpu_dp->tag_ops->overhead;-else-slave_dev->needed_headroom=cpu_dp->tag_ops->overhead;-/* Try to save one extra realloc later in the TX path (in the master)-*byalsoinheritingthemaster'sneededheadroomandtailroom.-*The8021qdriveralsodoesthis.-*/-slave_dev->needed_headroom+=master->needed_headroom;-slave_dev->needed_tailroom+=master->needed_tailroom;SET_NETDEV_DEVTYPE(slave_dev,&dsa_type);netdev_for_each_tx_queue(slave_dev,dsa_slave_set_lockdep_class_one,
@@ -1814,8 +1825,8 @@ int dsa_slave_create(struct dsa_port *port)p->dp=port;INIT_LIST_HEAD(&p->mall_tc_list);-p->xmit=cpu_dp->tag_ops->xmit;port->slave=slave_dev;+dsa_slave_setup_tagger(slave_dev);rtnl_lock();ret=dsa_slave_change_mtu(slave_dev,ETH_DATA_LEN);
@@ -297,6 +297,58 @@ static int dsa_switch_vlan_del(struct dsa_switch *ds,return0;}+staticbooldsa_switch_tag_proto_match(structdsa_switch*ds,intport,+structdsa_notifier_tag_proto_info*info)+{+if(dsa_is_cpu_port(ds,port)||dsa_is_dsa_port(ds,port))+returntrue;++returnfalse;+}++staticintdsa_switch_change_tag_proto(structdsa_switch*ds,+structdsa_notifier_tag_proto_info*info)+{+conststructdsa_device_ops*tag_ops=info->tag_ops;+intport,err;++if(!ds->ops->change_tag_protocol)+return-EOPNOTSUPP;++ASSERT_RTNL();++for(port=0;port<ds->num_ports;port++){+if(dsa_switch_tag_proto_match(ds,port,info)){+err=ds->ops->change_tag_protocol(ds,port,+tag_ops->proto);+if(err)+returnerr;++if(dsa_is_cpu_port(ds,port))+dsa_port_set_tag_protocol(dsa_to_port(ds,port),+tag_ops);+}+}++/* Now that changing the tag protocol can no longer fail, let's update+*theremainingbitswhichare"duplicated for faster access",andthe+*bitsthatdependonthetagger,suchastheMTU.+*/+for(port=0;port<ds->num_ports;port++){+if(dsa_is_user_port(ds,port)){+structnet_device*slave;++slave=dsa_to_port(ds,port)->slave;+dsa_slave_setup_tagger(slave);++/* rtnl_mutex is held in dsa_tree_change_tag_proto */+dsa_slave_change_mtu(slave,slave->mtu);+}+}++return0;+}+staticintdsa_switch_event(structnotifier_block*nb,unsignedlongevent,void*info){
@@ -343,6 +395,9 @@ static int dsa_switch_event(struct notifier_block *nb,caseDSA_NOTIFIER_MTU:err=dsa_switch_mtu(ds,info);break;+caseDSA_NOTIFIER_TAG_PROTO:+err=dsa_switch_change_tag_proto(ds,info);+break;default:err=-EOPNOTSUPP;break;
From: Vladimir Oltean <olteanv@gmail.com> Date: 2021-01-29 01:03:54
From: Vladimir Oltean <vladimir.oltean@nxp.com>
In expectation of the new tag_ocelot_8021q tagger implementation, we
need to be able to do runtime switchover between one tagger and another.
So we must structure the existing code for the current NPI-based tagger
in a certain way.
We move the felix_npi_port_init function in expectation of the future
driver configuration necessary for tag_ocelot_8021q: we would like to
not have the NPI-related bits interspersed with the tag_8021q bits.
The conversion from this:
ocelot_write_rix(ocelot,
ANA_PGID_PGID_PGID(GENMASK(ocelot->num_phys_ports, 0)),
ANA_PGID_PGID, PGID_UC);
to this:
cpu_flood = ANA_PGID_PGID_PGID(BIT(ocelot->num_phys_ports));
ocelot_rmw_rix(ocelot, cpu_flood, cpu_flood, ANA_PGID_PGID, PGID_UC);
is perhaps non-trivial, but is nonetheless non-functional. The PGID_UC
(replicator for unknown unicast) is already configured out of hardware
reset to flood to all ports except ocelot->num_phys_ports (the CPU port
module). All we change is that we use a read-modify-write to only add
the CPU port module to the unknown unicast replicator, as opposed to
doing a full write to the register.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Changes in v8:
- Used -EPROTONOSUPPORT instead of -EOPNOTSUPP as return code.
- Adapted to .change_tag_protocol. Kept felix_set_tag_protocol and
felix_del_tag_protocol, but now calling them privately from
felix_setup and felix_teardown.
- Removed ocelot_apply_bridge_fwd_mask from this patch and moved it to
11/11.
- Dropped review tags due to amount of changes.
Changes in v7:
None.
Changes in v6:
None.
Changes in v5:
Path is split from previous monolithic patch "net: dsa: felix: add new
VLAN-based tagger".
drivers/net/dsa/ocelot/felix.c | 186 ++++++++++++++++++-----
drivers/net/dsa/ocelot/felix.h | 1 +
drivers/net/dsa/ocelot/felix_vsc9959.c | 1 +
drivers/net/dsa/ocelot/seville_vsc9953.c | 1 +
4 files changed, 154 insertions(+), 35 deletions(-)
@@ -24,11 +24,140 @@#include<net/dsa.h>#include"felix.h"+/* The CPU port module is connected to the Node Processor Interface (NPI). This+*isthemodethroughwhichframescanbeinjectedfromandextractedtoan+*externalCPU,overEthernet.InNXPSoCs,the"external CPU"istheARMCPU+*runningLinux,andthisformsaDSAsetuptogetherwiththeenetcorfman+*DSAmaster.+*/+staticvoidfelix_npi_port_init(structocelot*ocelot,intport)+{+ocelot->npi=port;++ocelot_write(ocelot,QSYS_EXT_CPU_CFG_EXT_CPUQ_MSK_M|+QSYS_EXT_CPU_CFG_EXT_CPU_PORT(port),+QSYS_EXT_CPU_CFG);++/* NPI port Injection/Extraction configuration */+ocelot_fields_write(ocelot,port,SYS_PORT_MODE_INCL_XTR_HDR,+ocelot->npi_xtr_prefix);+ocelot_fields_write(ocelot,port,SYS_PORT_MODE_INCL_INJ_HDR,+ocelot->npi_inj_prefix);++/* Disable transmission of pause frames */+ocelot_fields_write(ocelot,port,SYS_PAUSE_CFG_PAUSE_ENA,0);+}++staticvoidfelix_npi_port_deinit(structocelot*ocelot,intport)+{+/* Restore hardware defaults */+intunused_port=ocelot->num_phys_ports+2;++ocelot->npi=-1;++ocelot_write(ocelot,QSYS_EXT_CPU_CFG_EXT_CPU_PORT(unused_port),+QSYS_EXT_CPU_CFG);++ocelot_fields_write(ocelot,port,SYS_PORT_MODE_INCL_XTR_HDR,+OCELOT_TAG_PREFIX_DISABLED);+ocelot_fields_write(ocelot,port,SYS_PORT_MODE_INCL_INJ_HDR,+OCELOT_TAG_PREFIX_DISABLED);++/* Enable transmission of pause frames */+ocelot_fields_write(ocelot,port,SYS_PAUSE_CFG_PAUSE_ENA,1);+}++staticintfelix_setup_tag_npi(structdsa_switch*ds,intcpu)+{+structocelot*ocelot=ds->priv;+unsignedlongcpu_flood;++felix_npi_port_init(ocelot,cpu);++/* Include the CPU port module (and indirectly, the NPI port)+*intheforwardingmaskforunknownunicast-thehardware+*defaultvalueforANA_FLOODING_FLD_UNICASTexcludes+*BIT(ocelot->num_phys_ports),andsodoesocelot_init,+*sinceOcelotreliesonwhitelistingMACaddressestowards+*PGID_CPU.+*WedothisbecauseDSAdoesnotyetperformRXfiltering,+*andtheNPIportdoesnotperformsourceaddresslearning,+*sotrafficsenttoLinuxiseffectivelyunknownfromthe+*switch'sperspective.+*/+cpu_flood=ANA_PGID_PGID_PGID(BIT(ocelot->num_phys_ports));+ocelot_rmw_rix(ocelot,cpu_flood,cpu_flood,ANA_PGID_PGID,PGID_UC);++return0;+}++staticvoidfelix_teardown_tag_npi(structdsa_switch*ds,intcpu)+{+structocelot*ocelot=ds->priv;++felix_npi_port_deinit(ocelot,cpu);+}++staticintfelix_set_tag_protocol(structdsa_switch*ds,intcpu,+enumdsa_tag_protocolproto)+{+interr;++switch(proto){+caseDSA_TAG_PROTO_OCELOT:+err=felix_setup_tag_npi(ds,cpu);+break;+default:+err=-EPROTONOSUPPORT;+}++returnerr;+}++staticvoidfelix_del_tag_protocol(structdsa_switch*ds,intcpu,+enumdsa_tag_protocolproto)+{+switch(proto){+caseDSA_TAG_PROTO_OCELOT:+felix_teardown_tag_npi(ds,cpu);+break;+default:+break;+}+}++staticintfelix_change_tag_protocol(structdsa_switch*ds,intcpu,+enumdsa_tag_protocolproto)+{+structocelot*ocelot=ds->priv;+structfelix*felix=ocelot_to_felix(ocelot);+enumdsa_tag_protocolold_proto=felix->tag_proto;+interr;++if(proto!=DSA_TAG_PROTO_OCELOT)+return-EPROTONOSUPPORT;++felix_del_tag_protocol(ds,cpu,old_proto);++err=felix_set_tag_protocol(ds,cpu,proto);+if(err){+felix_set_tag_protocol(ds,cpu,old_proto);+returnerr;+}++felix->tag_proto=proto;++return0;+}+staticenumdsa_tag_protocolfelix_get_tag_protocol(structdsa_switch*ds,intport,enumdsa_tag_protocolmp){-returnDSA_TAG_PROTO_OCELOT;+structocelot*ocelot=ds->priv;+structfelix*felix=ocelot_to_felix(ocelot);++returnfelix->tag_proto;}staticintfelix_set_ageing_time(structdsa_switch*ds,
@@ -527,28 +656,6 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)return0;}-/* The CPU port module is connected to the Node Processor Interface (NPI). This-*isthemodethroughwhichframescanbeinjectedfromandextractedtoan-*externalCPU,overEthernet.-*/-staticvoidfelix_npi_port_init(structocelot*ocelot,intport)-{-ocelot->npi=port;--ocelot_write(ocelot,QSYS_EXT_CPU_CFG_EXT_CPUQ_MSK_M|-QSYS_EXT_CPU_CFG_EXT_CPU_PORT(port),-QSYS_EXT_CPU_CFG);--/* NPI port Injection/Extraction configuration */-ocelot_fields_write(ocelot,port,SYS_PORT_MODE_INCL_XTR_HDR,-ocelot->npi_xtr_prefix);-ocelot_fields_write(ocelot,port,SYS_PORT_MODE_INCL_INJ_HDR,-ocelot->npi_inj_prefix);--/* Disable transmission of pause frames */-ocelot_fields_write(ocelot,port,SYS_PAUSE_CFG_PAUSE_ENA,0);-}-/* Hardware initialization done here so that we can allocate structures with*devmwithoutfearofdsa_register_switchreturning-EPROBE_DEFERandcausing*ustoallocatestructurestwice(leakmemory)andmapPCImemorytwice
@@ -578,10 +685,10 @@ static int felix_setup(struct dsa_switch *ds)}for(port=0;port<ds->num_ports;port++){-ocelot_init_port(ocelot,port);+if(dsa_is_unused_port(ds,port))+continue;-if(dsa_is_cpu_port(ds,port))-felix_npi_port_init(ocelot,port);+ocelot_init_port(ocelot,port);/* Set the default QoS Classification based on PCP and DEI*bitsofvlantag.
@@ -593,14 +700,15 @@ static int felix_setup(struct dsa_switch *ds)if(err)returnerr;-/* Include the CPU port module in the forwarding mask for unknown-*unicast-thehardwaredefaultvalueforANA_FLOODING_FLD_UNICAST-*excludesBIT(ocelot->num_phys_ports),andsodoesocelot_init,since-*OcelotreliesonwhitelistingMACaddressestowardsPGID_CPU.-*/-ocelot_write_rix(ocelot,-ANA_PGID_PGID_PGID(GENMASK(ocelot->num_phys_ports,0)),-ANA_PGID_PGID,PGID_UC);+for(port=0;port<ds->num_ports;port++){+if(!dsa_is_cpu_port(ds,port))+continue;++/* The initial tag protocol is NPI which always returns 0, so+*there'snorealpointincheckingforerrors.+*/+felix_set_tag_protocol(ds,port,felix->tag_proto);+}ds->mtu_enforcement_ingress=true;ds->assisted_learning_on_cpu_port=true;
From: Vladimir Oltean <olteanv@gmail.com> Date: 2021-01-29 01:04:00
From: Vladimir Oltean <vladimir.oltean@nxp.com>
Unlike sja1105, the only other user of the software-defined tag_8021q.c
tagger format, the implementation we choose for the Felix DSA switch
driver preserves full functionality under a vlan_filtering bridge
(i.e. IP termination works through the DSA user ports under all
circumstances).
The tag_8021q protocol just wants:
- Identifying the ingress switch port based on the RX VLAN ID, as seen
by the CPU. We achieve this by using the TCAM engines (which are also
used for tc-flower offload) to push the RX VLAN as a second, outer
tag, on egress towards the CPU port.
- Steering traffic injected into the switch from the network stack
towards the correct front port based on the TX VLAN, and consuming
(popping) that header on the switch's egress.
A tc-flower pseudocode of the static configuration done by the driver
would look like this:
$ tc qdisc add dev <cpu-port> clsact
$ for eth in swp0 swp1 swp2 swp3; do \
tc filter add dev <cpu-port> egress flower indev ${eth} \
action vlan push id <rxvlan> protocol 802.1ad; \
tc filter add dev <cpu-port> ingress protocol 802.1Q flower
vlan_id <txvlan> action vlan pop \
action mirred egress redirect dev ${eth}; \
done
but of course since DSA does not register network interfaces for the CPU
port, this configuration would be impossible for the user to do. Also,
due to the same reason, it is impossible for the user to inadvertently
delete these rules using tc. These rules do not collide in any way with
tc-flower, they just consume some TCAM space, which is something we can
live with.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
---
Changes in v8:
- Refactor ocelot_apply_bridge_fwd_mask.
- Call ocelot_apply_bridge_fwd_mask immediately after the is_dsa_8021q_cpu
variable changes, i.e. in felix_8021q_cpu_port_init and
felix_8021q_cpu_port_deinit.
Changes in v7:
Drop the rtnl_lock surrounding dsa_8021q_setup since some callers of
.{set,del}_tag_protocol now hold the rtnl_mutex and we'd run into a
deadlock if we took it. That's also why all callers needed to be
converted to hold the lock, since otherwise dsa_8021q_setup would have
no guarantees short of passing it a bool rtnl_is_held variable.
Changes in v6:
None.
Changes in v5:
Path is split from previous monolithic patch "net: dsa: felix: add new
VLAN-based tagger".
Changes in v4:
- Support simultaneous compilation of tag_ocelot.c and
tag_ocelot_8021q.c.
- Support runtime switchover between these two taggers.
- We are now actually performing cleanup instead of just probe-time
setup, which is required for supporting tagger switchover.
Changes in v3:
- Use a per-port bool is_dsa_8021q_cpu instead of a single dsa_8021q_cpu
variable, to be compatible with future work where there may be
potentially multiple tag_8021q CPU ports in a LAG.
- Initialize ocelot->npi = -1 in felix_8021q_cpu_port_init to ensure we
don't mistakenly trigger NPI-specific code in ocelot.
Changes in v2:
Clean up the hardcoding of random VCAP filter IDs and the inclusion of a
private ocelot header.
drivers/net/dsa/ocelot/felix.c | 337 +++++++++++++++++++++++-
drivers/net/dsa/ocelot/felix.h | 1 +
drivers/net/ethernet/mscc/ocelot.c | 61 ++++-
drivers/net/ethernet/mscc/ocelot_vcap.c | 1 +
drivers/net/ethernet/mscc/ocelot_vcap.h | 3 -
include/soc/mscc/ocelot.h | 2 +
include/soc/mscc/ocelot_vcap.h | 3 +
7 files changed, 396 insertions(+), 12 deletions(-)
@@ -24,6 +25,329 @@#include<net/dsa.h>#include"felix.h"+staticintfelix_tag_8021q_rxvlan_add(structfelix*felix,intport,u16vid,+boolpvid,booluntagged)+{+structocelot_vcap_filter*outer_tagging_rule;+structocelot*ocelot=&felix->ocelot;+structdsa_switch*ds=felix->ds;+intkey_length,upstream,err;++/* We don't need to install the rxvlan into the other ports' filtering+*tables,becausewe'rejustpushingtherxvlanwhensendingtowards+*theCPU+*/+if(!pvid)+return0;++key_length=ocelot->vcap[VCAP_ES0].keys[VCAP_ES0_IGR_PORT].length;+upstream=dsa_upstream_port(ds,port);++outer_tagging_rule=kzalloc(sizeof(structocelot_vcap_filter),+GFP_KERNEL);+if(!outer_tagging_rule)+return-ENOMEM;++outer_tagging_rule->key_type=OCELOT_VCAP_KEY_ANY;+outer_tagging_rule->prio=1;+outer_tagging_rule->id.cookie=port;+outer_tagging_rule->id.tc_offload=false;+outer_tagging_rule->block_id=VCAP_ES0;+outer_tagging_rule->type=OCELOT_VCAP_FILTER_OFFLOAD;+outer_tagging_rule->lookup=0;+outer_tagging_rule->ingress_port.value=port;+outer_tagging_rule->ingress_port.mask=GENMASK(key_length-1,0);+outer_tagging_rule->egress_port.value=upstream;+outer_tagging_rule->egress_port.mask=GENMASK(key_length-1,0);+outer_tagging_rule->action.push_outer_tag=OCELOT_ES0_TAG;+outer_tagging_rule->action.tag_a_tpid_sel=OCELOT_TAG_TPID_SEL_8021AD;+outer_tagging_rule->action.tag_a_vid_sel=1;+outer_tagging_rule->action.vid_a_val=vid;++err=ocelot_vcap_filter_add(ocelot,outer_tagging_rule,NULL);+if(err)+kfree(outer_tagging_rule);++returnerr;+}++staticintfelix_tag_8021q_txvlan_add(structfelix*felix,intport,u16vid,+boolpvid,booluntagged)+{+structocelot_vcap_filter*untagging_rule,*redirect_rule;+structocelot*ocelot=&felix->ocelot;+structdsa_switch*ds=felix->ds;+intupstream,err;++/* tag_8021q.c assumes we are implementing this via port VLAN+*membership,whichwearen't.Sowedon'tneedtoaddanyVCAPfilter+*fortheCPUport.+*/+if(ocelot->ports[port]->is_dsa_8021q_cpu)+return0;++untagging_rule=kzalloc(sizeof(structocelot_vcap_filter),GFP_KERNEL);+if(!untagging_rule)+return-ENOMEM;++redirect_rule=kzalloc(sizeof(structocelot_vcap_filter),GFP_KERNEL);+if(!redirect_rule){+kfree(untagging_rule);+return-ENOMEM;+}++upstream=dsa_upstream_port(ds,port);++untagging_rule->key_type=OCELOT_VCAP_KEY_ANY;+untagging_rule->ingress_port_mask=BIT(upstream);+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.tc_offload=false;+untagging_rule->block_id=VCAP_IS1;+untagging_rule->type=OCELOT_VCAP_FILTER_OFFLOAD;+untagging_rule->lookup=0;+untagging_rule->action.vlan_pop_cnt_ena=true;+untagging_rule->action.vlan_pop_cnt=1;+untagging_rule->action.pag_override_mask=0xff;+untagging_rule->action.pag_val=port;++err=ocelot_vcap_filter_add(ocelot,untagging_rule,NULL);+if(err){+kfree(untagging_rule);+kfree(redirect_rule);+returnerr;+}++redirect_rule->key_type=OCELOT_VCAP_KEY_ANY;+redirect_rule->ingress_port_mask=BIT(upstream);+redirect_rule->pag=port;+redirect_rule->prio=1;+redirect_rule->id.cookie=port;+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.mask_mode=OCELOT_MASK_MODE_REDIRECT;+redirect_rule->action.port_mask=BIT(port);++err=ocelot_vcap_filter_add(ocelot,redirect_rule,NULL);+if(err){+ocelot_vcap_filter_del(ocelot,untagging_rule);+kfree(redirect_rule);+returnerr;+}++return0;+}++staticintfelix_tag_8021q_vlan_add(structdsa_switch*ds,intport,u16vid,+u16flags)+{+booluntagged=flags&BRIDGE_VLAN_INFO_UNTAGGED;+boolpvid=flags&BRIDGE_VLAN_INFO_PVID;+structocelot*ocelot=ds->priv;++if(vid_is_dsa_8021q_rxvlan(vid))+returnfelix_tag_8021q_rxvlan_add(ocelot_to_felix(ocelot),+port,vid,pvid,untagged);++if(vid_is_dsa_8021q_txvlan(vid))+returnfelix_tag_8021q_txvlan_add(ocelot_to_felix(ocelot),+port,vid,pvid,untagged);++return0;+}++staticintfelix_tag_8021q_rxvlan_del(structfelix*felix,intport,u16vid)+{+structocelot_vcap_filter*outer_tagging_rule;+structocelot_vcap_block*block_vcap_es0;+structocelot*ocelot=&felix->ocelot;++block_vcap_es0=&ocelot->block[VCAP_ES0];++outer_tagging_rule=ocelot_vcap_block_find_filter_by_id(block_vcap_es0,+port,false);+/* In rxvlan_add, we had the "if (!pvid) return 0" logic to avoid+*installingoutertaggingES0ruleswheretheyweren'tneeded.+*Butinrxvlan_del,theAPIdoesn'tgiveusthe"flags"anymore,+*sothatforcesustobeslightlysloppyhere,andjustassumethat+*ifwedidn'tfindanouter_tagging_ruleitmeansthattherewas+*noneinthefirstplace,i.e.rxvlan_deliscalledonanon-pvid+*port.Thisismostprobablytruethough.+*/+if(!outer_tagging_rule)+return0;++returnocelot_vcap_filter_del(ocelot,outer_tagging_rule);+}++staticintfelix_tag_8021q_txvlan_del(structfelix*felix,intport,u16vid)+{+structocelot_vcap_filter*untagging_rule,*redirect_rule;+structocelot_vcap_block*block_vcap_is1;+structocelot_vcap_block*block_vcap_is2;+structocelot*ocelot=&felix->ocelot;+interr;++if(ocelot->ports[port]->is_dsa_8021q_cpu)+return0;++block_vcap_is1=&ocelot->block[VCAP_IS1];+block_vcap_is2=&ocelot->block[VCAP_IS2];++untagging_rule=ocelot_vcap_block_find_filter_by_id(block_vcap_is1,+port,false);+if(!untagging_rule)+return0;++err=ocelot_vcap_filter_del(ocelot,untagging_rule);+if(err)+returnerr;++redirect_rule=ocelot_vcap_block_find_filter_by_id(block_vcap_is2,+port,false);+if(!redirect_rule)+return0;++returnocelot_vcap_filter_del(ocelot,redirect_rule);+}++staticintfelix_tag_8021q_vlan_del(structdsa_switch*ds,intport,u16vid)+{+structocelot*ocelot=ds->priv;++if(vid_is_dsa_8021q_rxvlan(vid))+returnfelix_tag_8021q_rxvlan_del(ocelot_to_felix(ocelot),+port,vid);++if(vid_is_dsa_8021q_txvlan(vid))+returnfelix_tag_8021q_txvlan_del(ocelot_to_felix(ocelot),+port,vid);++return0;+}++staticconststructdsa_8021q_opsfelix_tag_8021q_ops={+.vlan_add=felix_tag_8021q_vlan_add,+.vlan_del=felix_tag_8021q_vlan_del,+};++/* Alternatively to using the NPI functionality, that same hardware MAC+*connectedinternallytotheenetcorfmanDSAmastercanbeconfiguredto+*usethesoftware-definedtag_8021qframeformat.Asfarasthehardwareis+*concerned,itthinksitisa"dumb switch"-thequeuesoftheCPUport+*modulearenowdisconnectedfromit,butcanstillbeaccessedthrough+*register-basedMMIO.+*/+staticvoidfelix_8021q_cpu_port_init(structocelot*ocelot,intport)+{+ocelot->ports[port]->is_dsa_8021q_cpu=true;+ocelot->npi=-1;++/* Overwrite PGID_CPU with the non-tagging port */+ocelot_write_rix(ocelot,BIT(port),ANA_PGID_PGID,PGID_CPU);++ocelot_apply_bridge_fwd_mask(ocelot);+}++staticvoidfelix_8021q_cpu_port_deinit(structocelot*ocelot,intport)+{+ocelot->ports[port]->is_dsa_8021q_cpu=false;++/* Restore PGID_CPU */+ocelot_write_rix(ocelot,BIT(ocelot->num_phys_ports),ANA_PGID_PGID,+PGID_CPU);++ocelot_apply_bridge_fwd_mask(ocelot);+}++staticintfelix_setup_tag_8021q(structdsa_switch*ds,intcpu)+{+structocelot*ocelot=ds->priv;+structfelix*felix=ocelot_to_felix(ocelot);+unsignedlongcpu_flood;+intport,err;++felix_8021q_cpu_port_init(ocelot,cpu);++for(port=0;port<ds->num_ports;port++){+if(dsa_is_unused_port(ds,port))+continue;++/* This overwrites ocelot_init():+*DonotforwardBPDUframestotheCPUportmodule,+*for2reasons:+*-Whenthesepacketsareinjectedfromthetag_8021q+*CPUport,wewantthemtogoout,notloopback+*intothesystem.+*-STPtrafficingressingonauserportshouldgoto+*thetag_8021qCPUport,nottothehardwareCPU+*portmodule.+*/+ocelot_write_gix(ocelot,+ANA_PORT_CPU_FWD_BPDU_CFG_BPDU_REDIR_ENA(0),+ANA_PORT_CPU_FWD_BPDU_CFG,port);+}++/* In tag_8021q mode, the CPU port module is unused. So we+*wanttodisablefloodingofanykindtotheCPUportmodule,+*sincepacketsgoingtherewillendinablackhole.+*/+cpu_flood=ANA_PGID_PGID_PGID(BIT(ocelot->num_phys_ports));+ocelot_rmw_rix(ocelot,0,cpu_flood,ANA_PGID_PGID,PGID_UC);+ocelot_rmw_rix(ocelot,0,cpu_flood,ANA_PGID_PGID,PGID_MC);++felix->dsa_8021q_ctx=kzalloc(sizeof(*felix->dsa_8021q_ctx),+GFP_KERNEL);+if(!felix->dsa_8021q_ctx)+return-ENOMEM;++felix->dsa_8021q_ctx->ops=&felix_tag_8021q_ops;+felix->dsa_8021q_ctx->proto=htons(ETH_P_8021AD);+felix->dsa_8021q_ctx->ds=ds;++err=dsa_8021q_setup(felix->dsa_8021q_ctx,true);+if(err)+gotoout_free_dsa_8021_ctx;++return0;++out_free_dsa_8021_ctx:+kfree(felix->dsa_8021q_ctx);+returnerr;+}++staticvoidfelix_teardown_tag_8021q(structdsa_switch*ds,intcpu)+{+structocelot*ocelot=ds->priv;+structfelix*felix=ocelot_to_felix(ocelot);+interr,port;++err=dsa_8021q_setup(felix->dsa_8021q_ctx,false);+if(err)+dev_err(ds->dev,"dsa_8021q_setup returned %d",err);++kfree(felix->dsa_8021q_ctx);++for(port=0;port<ds->num_ports;port++){+if(dsa_is_unused_port(ds,port))+continue;++/* Restore the logic from ocelot_init:+*donotforwardBPDUframestothefrontports.+*/+ocelot_write_gix(ocelot,+ANA_PORT_CPU_FWD_BPDU_CFG_BPDU_REDIR_ENA(0xffff),+ANA_PORT_CPU_FWD_BPDU_CFG,+port);+}++felix_8021q_cpu_port_deinit(ocelot,cpu);+}+/* The CPU port module is connected to the Node Processor Interface (NPI). This*isthemodethroughwhichframescanbeinjectedfromandextractedtoan*externalCPU,overEthernet.InNXPSoCs,the"external CPU"istheARMCPU
@@ -107,6 +431,9 @@ static int felix_set_tag_protocol(struct dsa_switch *ds, int cpu,caseDSA_TAG_PROTO_OCELOT:err=felix_setup_tag_npi(ds,cpu);break;+caseDSA_TAG_PROTO_OCELOT_8021Q:+err=felix_setup_tag_8021q(ds,cpu);+break;default:err=-EPROTONOSUPPORT;}
@@ -121,11 +448,18 @@ static void felix_del_tag_protocol(struct dsa_switch *ds, int cpu,caseDSA_TAG_PROTO_OCELOT:felix_teardown_tag_npi(ds,cpu);break;+caseDSA_TAG_PROTO_OCELOT_8021Q:+felix_teardown_tag_8021q(ds,cpu);+break;default:break;}}+/* This always leaves the switch in a consistent state, because although the+*tag_8021qsetupcanfail,theNPIsetupcan't.Soeitherthechangeismade,+*ortherestorationisguaranteedtowork.+*/staticintfelix_change_tag_protocol(structdsa_switch*ds,intcpu,enumdsa_tag_protocolproto){
@@ -134,7 +468,8 @@ static int felix_change_tag_protocol(struct dsa_switch *ds, int cpu,enumdsa_tag_protocolold_proto=felix->tag_proto;interr;-if(proto!=DSA_TAG_PROTO_OCELOT)+if(proto!=DSA_TAG_PROTO_OCELOT&&+proto!=DSA_TAG_PROTO_OCELOT_8021Q)return-EPROTONOSUPPORT;felix_del_tag_protocol(ds,cpu,old_proto);
@@ -889,18 +889,60 @@ int ocelot_get_ts_info(struct ocelot *ocelot, int port,}EXPORT_SYMBOL(ocelot_get_ts_info);-staticvoidocelot_apply_bridge_fwd_mask(structocelot*ocelot)+staticu32ocelot_get_dsa_8021q_cpu_mask(structocelot*ocelot){+u32mask=0;intport;+for(port=0;port<ocelot->num_phys_ports;port++){+structocelot_port*ocelot_port=ocelot->ports[port];++if(!ocelot_port)+continue;++if(ocelot_port->is_dsa_8021q_cpu)+mask|=BIT(port);+}++returnmask;+}++voidocelot_apply_bridge_fwd_mask(structocelot*ocelot)+{+unsignedlongcpu_fwd_mask;+intport;++/* If a DSA tag_8021q CPU exists, it needs to be included in the+*regularforwardingpathofthefrontportsregardlessofwhether+*thosearebridgedorstandalone.+*IfDSAtag_8021qisnotused,thisreturns0,whichisfinebecause+*thehardware-basedCPUportmodulecanbeadestinationforpackets+*evenifitisn'tpartofPGID_SRC.+*/+cpu_fwd_mask=ocelot_get_dsa_8021q_cpu_mask(ocelot);+/* Apply FWD mask. The loop is needed to add/remove the current port as*asourcefortheotherports.*/for(port=0;port<ocelot->num_phys_ports;port++){-if(ocelot->bridge_fwd_mask&BIT(port)){-unsignedlongmask=ocelot->bridge_fwd_mask&~BIT(port);+structocelot_port*ocelot_port=ocelot->ports[port];+unsignedlongmask;++if(!ocelot_port){+/* Unused ports can't send anywhere */+mask=0;+}elseif(ocelot_port->is_dsa_8021q_cpu){+/* The DSA tag_8021q CPU ports need to be able to+*forwardpacketstoallotherportsexceptfor+*themselves+*/+mask=GENMASK(ocelot->num_phys_ports-1,0);+mask&=~cpu_fwd_mask;+}elseif(ocelot->bridge_fwd_mask&BIT(port)){intlag;+mask=ocelot->bridge_fwd_mask&~BIT(port);+for(lag=0;lag<ocelot->num_phys_ports;lag++){unsignedlongbond_mask=ocelot->lags[lag];
@@ -912,15 +954,18 @@ static void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot)break;}}--ocelot_write_rix(ocelot,mask,-ANA_PGID_PGID,PGID_SRC+port);}else{-ocelot_write_rix(ocelot,0,-ANA_PGID_PGID,PGID_SRC+port);+/* Standalone ports forward only to DSA tag_8021q CPU+*ports(ifthoseexist),ortothehardwareCPUport+*moduleotherwise.+*/+mask=cpu_fwd_mask;}++ocelot_write_rix(ocelot,mask,ANA_PGID_PGID,PGID_SRC+port);}}+EXPORT_SYMBOL(ocelot_apply_bridge_fwd_mask);voidocelot_bridge_stp_state_set(structocelot*ocelot,intport,u8state){
@@ -1009,6 +1009,7 @@ ocelot_vcap_block_find_filter_by_id(struct ocelot_vcap_block *block, int cookie,returnNULL;}+EXPORT_SYMBOL(ocelot_vcap_block_find_filter_by_id);/* If @on=false, then SNAP, ARP, IP and OAM frames will not match on keys based*ondestinationandsourceMACaddresses,butonlyonhigher-levelprotocol
From: Vladimir Oltean <olteanv@gmail.com> Date: 2021-01-29 01:04:36
From: Vladimir Oltean <vladimir.oltean@nxp.com>
There are use cases for which the existing tagger, based on the NPI
(Node Processor Interface) functionality, is insufficient.
Namely:
- Frames injected through the NPI port bypass the frame analyzer, so no
source address learning is performed, no TSN stream classification,
etc.
- Flow control is not functional over an NPI port (PAUSE frames are
encapsulated in the same Extraction Frame Header as all other frames)
- There can be at most one NPI port configured for an Ocelot switch. But
in NXP LS1028A and T1040 there are two Ethernet CPU ports. The non-NPI
port is currently either disabled, or operated as a plain user port
(albeit an internally-facing one). Having the ability to configure the
two CPU ports symmetrically could pave the way for e.g. creating a LAG
between them, to increase bandwidth seamlessly for the system.
So there is a desire to have an alternative to the NPI mode. This change
keeps the default tagger for the Seville and Felix switches as "ocelot",
but it can be changed via the following device attribute:
echo ocelot-8021q > /sys/class/<dsa-master>/dsa/tagging
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
---
Changes in v7:
Made struct dsa_device_ops const and gave it a unique name to
distinguish from the NPI-based tagger.
Changes in v6:
None.
Changes in v5:
Path is split from previous monolithic patch "net: dsa: felix: add new
VLAN-based tagger".
MAINTAINERS | 1 +
drivers/net/dsa/ocelot/Kconfig | 2 +
include/net/dsa.h | 2 +
net/dsa/Kconfig | 21 +++++++++--
net/dsa/Makefile | 1 +
net/dsa/tag_ocelot_8021q.c | 68 ++++++++++++++++++++++++++++++++++
6 files changed, 92 insertions(+), 3 deletions(-)
create mode 100644 net/dsa/tag_ocelot_8021q.c
@@ -105,11 +105,26 @@ config NET_DSA_TAG_RTL4_AtheRealtekRTL8366RB.configNET_DSA_TAG_OCELOT-tristate"Tag driver for Ocelot family of switches"+tristate"Tag driver for Ocelot family of switches, using NPI port"selectPACKINGhelp-SayYorMifyouwanttoenablesupportfortaggingframesforthe-Ocelotswitches(VSC7511,VSC7512,VSC7513,VSC7514,VSC9959).+SayYorMifyouwanttoenableNPItaggingfortheOcelotswitches+(VSC7511,VSC7512,VSC7513,VSC7514,VSC9953,VSC9959).Inthismode,+theframesovertheEthernetCPUportareprependedwitha+hardware-definedinjection/extractionframeheader.Flowcontrol+(PAUSEframes)overtheCPUportisnotsupportedwhenoperatingin+thismode.++configNET_DSA_TAG_OCELOT_8021Q+tristate"Tag driver for Ocelot family of switches, using VLAN"+selectNET_DSA_TAG_8021Q+help+SayYorMifyouwanttoenablesupportfortaggingframeswitha+customVLAN-basedheader.Framesthatrequiretimestamping,suchas+PTP,arenotdeliveredoverEthernetbutoverregister-basedMMIO.+FlowcontrolovertheCPUportisfunctionalinthismode.Whenusing+thismode,lessTCAMresources(VCAPIS1,IS2,ES0)areavailablefor+usewithtc-flower.configNET_DSA_TAG_QCAtristate"Tag driver for Qualcomm Atheros QCA8K switches"
From: Vladimir Oltean <olteanv@gmail.com> Date: 2021-01-29 23:44:07
On Fri, Jan 29, 2021 at 03:00:06AM +0200, Vladimir Oltean wrote:
quoted hunk
From: Vladimir Oltean <vladimir.oltean@nxp.com>
Currently DSA exposes the following sysfs:
$ cat /sys/class/net/eno2/dsa/tagging
ocelot
which is a read-only device attribute, introduced in the kernel as
commit 98cdb4807123 ("net: dsa: Expose tagging protocol to user-space"),
and used by libpcap since its commit 993db3800d7d ("Add support for DSA
link-layer types").
It would be nice if we could extend this device attribute by making it
writable:
$ echo ocelot-8021q > /sys/class/net/eno2/dsa/tagging
This is useful with DSA switches that can make use of more than one
tagging protocol. It may be useful in dsa_loop in the future too, to
perform offline testing of various taggers, or for changing between dsa
and edsa on Marvell switches, if that is desirable.
In terms of implementation, drivers can support this feature by
implementing .change_tag_protocol, which should always leave the switch
in a consistent state: either with the new protocol if things went well,
or with the old one if something failed. Teardown of the old protocol,
if necessary, must be handled by the driver.
Some things remain as before:
- The .get_tag_protocol is currently only called at probe time, to load
the initial tagging protocol driver. Nonetheless, new drivers should
report the tagging protocol in current use now.
- The driver should manage by itself the initial setup of tagging
protocol, no later than the .setup() method, as well as destroying
resources used by the last tagger in use, no earlier than the
.teardown() method.
For multi-switch DSA trees, error handling is a bit more complicated,
since e.g. the 5th out of 7 switches may fail to change the tag
protocol. When that happens, a revert to the original tag protocol is
attempted, but that may fail too, leaving the tree in an inconsistent
state despite each individual switch implementing .change_tag_protocol
transactionally. Since the intersection between drivers that implement
.change_tag_protocol and drivers that support D in DSA is currently the
empty set, the possibility for this error to happen is ignored for now.
Testing:
$ insmod mscc_felix.ko
[ 79.549784] mscc_felix 0000:00:00.5: Adding to iommu group 14
[ 79.565712] mscc_felix 0000:00:00.5: Failed to register DSA switch: -517
$ insmod tag_ocelot.ko
$ rmmod mscc_felix.ko
$ insmod mscc_felix.ko
[ 97.261724] libphy: VSC9959 internal MDIO bus: probed
[ 97.267363] mscc_felix 0000:00:00.5: Found PCS at internal MDIO address 0
[ 97.274998] mscc_felix 0000:00:00.5: Found PCS at internal MDIO address 1
[ 97.282561] mscc_felix 0000:00:00.5: Found PCS at internal MDIO address 2
[ 97.289700] mscc_felix 0000:00:00.5: Found PCS at internal MDIO address 3
[ 97.599163] mscc_felix 0000:00:00.5 swp0 (uninitialized): PHY [0000:00:00.3:10] driver [Microsemi GE VSC8514 SyncE] (irq=POLL)
[ 97.862034] mscc_felix 0000:00:00.5 swp1 (uninitialized): PHY [0000:00:00.3:11] driver [Microsemi GE VSC8514 SyncE] (irq=POLL)
[ 97.950731] mscc_felix 0000:00:00.5 swp0: configuring for inband/qsgmii link mode
[ 97.964278] 8021q: adding VLAN 0 to HW filter on device swp0
[ 98.146161] mscc_felix 0000:00:00.5 swp2 (uninitialized): PHY [0000:00:00.3:12] driver [Microsemi GE VSC8514 SyncE] (irq=POLL)
[ 98.238649] mscc_felix 0000:00:00.5 swp1: configuring for inband/qsgmii link mode
[ 98.251845] 8021q: adding VLAN 0 to HW filter on device swp1
[ 98.433916] mscc_felix 0000:00:00.5 swp3 (uninitialized): PHY [0000:00:00.3:13] driver [Microsemi GE VSC8514 SyncE] (irq=POLL)
[ 98.485542] mscc_felix 0000:00:00.5: configuring for fixed/internal link mode
[ 98.503584] mscc_felix 0000:00:00.5: Link is Up - 2.5Gbps/Full - flow control rx/tx
[ 98.527948] device eno2 entered promiscuous mode
[ 98.544755] DSA: tree 0 setup
$ ping 10.0.0.1
PING 10.0.0.1 (10.0.0.1): 56 data bytes
64 bytes from 10.0.0.1: seq=0 ttl=64 time=2.337 ms
64 bytes from 10.0.0.1: seq=1 ttl=64 time=0.754 ms
^C
--- 10.0.0.1 ping statistics ---
Jakub, I was stupid and I pasted the ping command output into the commit
message, so git will trim anything past the dotted line as not part of
the commit message, which makes your netdev/verify_signedoff test fail.
If by some sort of miracle I don't need to resend a v9, do you think you
could just delete this and the next 2 lines?
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.754/1.545/2.337 ms
$ cat /sys/class/net/eno2/dsa/tagging
ocelot
$ cat ./test_ocelot_8021q.sh
#!/bin/bash
ip link set swp0 down
ip link set swp1 down
ip link set swp2 down
ip link set swp3 down
ip link set swp5 down
ip link set eno2 down
echo ocelot-8021q > /sys/class/net/eno2/dsa/tagging
ip link set eno2 up
ip link set swp0 up
ip link set swp1 up
ip link set swp2 up
ip link set swp3 up
ip link set swp5 up
$ ./test_ocelot_8021q.sh
./test_ocelot_8021q.sh: line 9: echo: write error: Protocol not available
$ rmmod tag_ocelot.ko
rmmod: can't unload module 'tag_ocelot': Resource temporarily unavailable
$ insmod tag_ocelot_8021q.ko
$ ./test_ocelot_8021q.sh
$ cat /sys/class/net/eno2/dsa/tagging
ocelot-8021q
$ rmmod tag_ocelot.ko
$ rmmod tag_ocelot_8021q.ko
rmmod: can't unload module 'tag_ocelot_8021q': Resource temporarily unavailable
$ ping 10.0.0.1
PING 10.0.0.1 (10.0.0.1): 56 data bytes
64 bytes from 10.0.0.1: seq=0 ttl=64 time=0.953 ms
64 bytes from 10.0.0.1: seq=1 ttl=64 time=0.787 ms
64 bytes from 10.0.0.1: seq=2 ttl=64 time=0.771 ms
$ rmmod mscc_felix.ko
[ 645.544426] mscc_felix 0000:00:00.5: Link is Down
[ 645.838608] DSA: tree 0 torn down
$ rmmod tag_ocelot_8021q.ko
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Changes in v8:
- Take reference on tagging driver module in dsa_find_tagger_by_name.
- Replaced DSA_NOTIFIER_TAG_PROTO_SET and DSA_NOTIFIER_TAG_PROTO_DEL
with a single DSA_NOTIFIER_TAG_PROTO.
- Combined .set_tag_protocol and .del_tag_protocol into a single
.change_tag_protocol, and we're no longer calling those 2 functions at
probe and unbind time.
- Dropped review tags due to amount of changes.
Hello:
This series was applied to netdev/net-next.git (refs/heads/master):
On Fri, 29 Jan 2021 02:59:58 +0200 you wrote:
From: Vladimir Oltean <vladimir.oltean@nxp.com>
Changes in v8:
- Make tagging driver module reference counting work per DSA switch tree
instead of per CPU port, to be compatible with the tag protocol changing
through sysfs.
- Refactor ocelot_apply_bridge_fwd_mask and call it immediately after
the is_dsa_8021q_cpu variable changes, i.e. in
felix_8021q_cpu_port_init and felix_8021q_cpu_port_deinit.
- Take reference on tagging driver module in dsa_find_tagger_by_name.
- Replaced DSA_NOTIFIER_TAG_PROTO_SET and DSA_NOTIFIER_TAG_PROTO_DEL
with a single DSA_NOTIFIER_TAG_PROTO.
- Combined .set_tag_protocol and .del_tag_protocol into a single
.change_tag_protocol, and we're no longer calling those 2 functions at
probe and unbind time.
- Adapted Felix to .change_tag_protocol. Kept felix_set_tag_protocol and
felix_del_tag_protocol, but now calling them privately from
felix_setup and felix_teardown.
- Used -EPROTONOSUPPORT instead of -EOPNOTSUPP as return code.
- Dropped some review tags due to amount of changes.
[...]
From: Jakub Kicinski <kuba@kernel.org> Date: 2021-01-30 10:24:09
On Sat, 30 Jan 2021 01:43:04 +0200 Vladimir Oltean wrote:
Jakub, I was stupid and I pasted the ping command output into the commit
message, so git will trim anything past the dotted line as not part of
the commit message, which makes your netdev/verify_signedoff test fail.
If by some sort of miracle I don't need to resend a v9, do you think you
could just delete this and the next 2 lines?