mbox series

[net-next,00/10] DSA unicast filtering

Message ID 20220302191417.1288145-1-vladimir.oltean@nxp.com (mailing list archive)
Headers show
Series DSA unicast filtering | expand

Message

Vladimir Oltean March 2, 2022, 7:14 p.m. UTC
This series doesn't attempt anything extremely brave, it just changes
the way in which standalone ports which support FDB isolation work.

Up until now, DSA has recommended that switch drivers configure
standalone ports in a separate VID/FID with learning disabled, and with
the CPU port as the only destination, reached trivially via flooding.
That works, except that standalone ports will deliver all packets to the
CPU. We can leverage the hardware FDB as a MAC DA filter, and disable
flooding towards the CPU port, to force the dropping of packets with
unknown MAC DA.

We handle port promiscuity by re-enabling flooding towards the CPU port.
This is relevant because the bridge puts its automatic (learning +
flooding) ports in promiscuous mode, and this makes some things work
automagically, like for example bridging with a foreign interface.
We don't delve yet into the territory of managing CPU flooding more
aggressively while under a bridge.

The only switch driver that benefits from this work right now is the
NXP LS1028A switch (felix). The others need to implement FDB isolation
first, before DSA is going to install entries to the port's standalone
database. Otherwise, these entries might collide with bridge FDB/MDB
entries.

This work was done mainly to have all the required features in place
before somebody starts seriously architecting DSA support for multiple
CPU ports. Otherwise it is much more difficult to bolt these features on
top of multiple CPU ports.

Vladimir Oltean (10):
  net: dsa: remove workarounds for changing master promisc/allmulti only
    while up
  net: dsa: rename the host FDB and MDB methods to contain the "bridge"
    namespace
  net: dsa: install secondary unicast and multicast addresses as host
    FDB/MDB
  net: dsa: install the primary unicast MAC address as standalone port
    host FDB
  net: dsa: manage flooding on the CPU ports
  net: dsa: felix: migrate host FDB and MDB entries when changing tag
    proto
  net: dsa: felix: migrate flood settings from NPI to tag_8021q CPU port
  net: dsa: felix: start off with flooding disabled on the CPU port
  net: dsa: felix: stop clearing CPU flooding in felix_setup_tag_8021q
  net: mscc: ocelot: accept configuring bridge port flags on the NPI
    port

 drivers/net/dsa/ocelot/felix.c     | 241 ++++++++++++++++++++------
 drivers/net/ethernet/mscc/ocelot.c |   3 +
 include/net/dsa.h                  |   7 +
 net/dsa/dsa.c                      |  40 +++++
 net/dsa/dsa_priv.h                 |  53 +++++-
 net/dsa/port.c                     | 160 +++++++++++++-----
 net/dsa/slave.c                    | 261 +++++++++++++++++++++++------
 7 files changed, 609 insertions(+), 156 deletions(-)

Comments

Florian Fainelli March 2, 2022, 7:30 p.m. UTC | #1
Hi Vladimir,

On 3/2/2022 11:14 AM, Vladimir Oltean wrote:
> This series doesn't attempt anything extremely brave, it just changes
> the way in which standalone ports which support FDB isolation work.
> 
> Up until now, DSA has recommended that switch drivers configure
> standalone ports in a separate VID/FID with learning disabled, and with
> the CPU port as the only destination, reached trivially via flooding.
> That works, except that standalone ports will deliver all packets to the
> CPU. We can leverage the hardware FDB as a MAC DA filter, and disable
> flooding towards the CPU port, to force the dropping of packets with
> unknown MAC DA.
> 
> We handle port promiscuity by re-enabling flooding towards the CPU port.
> This is relevant because the bridge puts its automatic (learning +
> flooding) ports in promiscuous mode, and this makes some things work
> automagically, like for example bridging with a foreign interface.
> We don't delve yet into the territory of managing CPU flooding more
> aggressively while under a bridge.
> 
> The only switch driver that benefits from this work right now is the
> NXP LS1028A switch (felix). The others need to implement FDB isolation
> first, before DSA is going to install entries to the port's standalone
> database. Otherwise, these entries might collide with bridge FDB/MDB
> entries.
> 
> This work was done mainly to have all the required features in place
> before somebody starts seriously architecting DSA support for multiple
> CPU ports. Otherwise it is much more difficult to bolt these features on
> top of multiple CPU ports.

Thanks a lot for submitting this, really happy to see a solution being 
brought upstream. I will be reviewing this in more details later on, but 
from where I left a few years ago, the two challenges that I had are 
outlined below, and I believe we have not quite addressed them yet:

- for switches that implement global VLAN filtering, upper VLAN 
interfaces on top of standalone ports would require programming FDB and 
MDB entries with the appropriate VLAN ID, however there is no such 
tracking today AFAICT, so we are not yet solving those use cases yet, right?

- what if the switch does not support FDB/MDB isolation, what would be 
our options here? As you might remember from a few months ago, the 
Broadcom roboswitch do not have any isolation, but what they can do is 
internally tag Ethernet frames with two VLAN tags, an that may be used 
as a form of isolation

> 
> Vladimir Oltean (10):
>    net: dsa: remove workarounds for changing master promisc/allmulti only
>      while up
>    net: dsa: rename the host FDB and MDB methods to contain the "bridge"
>      namespace
>    net: dsa: install secondary unicast and multicast addresses as host
>      FDB/MDB
>    net: dsa: install the primary unicast MAC address as standalone port
>      host FDB
>    net: dsa: manage flooding on the CPU ports
>    net: dsa: felix: migrate host FDB and MDB entries when changing tag
>      proto
>    net: dsa: felix: migrate flood settings from NPI to tag_8021q CPU port
>    net: dsa: felix: start off with flooding disabled on the CPU port
>    net: dsa: felix: stop clearing CPU flooding in felix_setup_tag_8021q
>    net: mscc: ocelot: accept configuring bridge port flags on the NPI
>      port
> 
>   drivers/net/dsa/ocelot/felix.c     | 241 ++++++++++++++++++++------
>   drivers/net/ethernet/mscc/ocelot.c |   3 +
>   include/net/dsa.h                  |   7 +
>   net/dsa/dsa.c                      |  40 +++++
>   net/dsa/dsa_priv.h                 |  53 +++++-
>   net/dsa/port.c                     | 160 +++++++++++++-----
>   net/dsa/slave.c                    | 261 +++++++++++++++++++++++------
>   7 files changed, 609 insertions(+), 156 deletions(-)
>
Vladimir Oltean March 2, 2022, 10:05 p.m. UTC | #2
Hi Florian,

On Wed, Mar 02, 2022 at 11:30:41AM -0800, Florian Fainelli wrote:
> Hi Vladimir,
> 
> On 3/2/2022 11:14 AM, Vladimir Oltean wrote:
> > This series doesn't attempt anything extremely brave, it just changes
> > the way in which standalone ports which support FDB isolation work.
> > 
> > Up until now, DSA has recommended that switch drivers configure
> > standalone ports in a separate VID/FID with learning disabled, and with
> > the CPU port as the only destination, reached trivially via flooding.
> > That works, except that standalone ports will deliver all packets to the
> > CPU. We can leverage the hardware FDB as a MAC DA filter, and disable
> > flooding towards the CPU port, to force the dropping of packets with
> > unknown MAC DA.
> > 
> > We handle port promiscuity by re-enabling flooding towards the CPU port.
> > This is relevant because the bridge puts its automatic (learning +
> > flooding) ports in promiscuous mode, and this makes some things work
> > automagically, like for example bridging with a foreign interface.
> > We don't delve yet into the territory of managing CPU flooding more
> > aggressively while under a bridge.
> > 
> > The only switch driver that benefits from this work right now is the
> > NXP LS1028A switch (felix). The others need to implement FDB isolation
> > first, before DSA is going to install entries to the port's standalone
> > database. Otherwise, these entries might collide with bridge FDB/MDB
> > entries.
> > 
> > This work was done mainly to have all the required features in place
> > before somebody starts seriously architecting DSA support for multiple
> > CPU ports. Otherwise it is much more difficult to bolt these features on
> > top of multiple CPU ports.
> 
> Thanks a lot for submitting this, really happy to see a solution being
> brought upstream. I will be reviewing this in more details later on, but
> from where I left a few years ago, the two challenges that I had are
> outlined below, and I believe we have not quite addressed them yet:
> 
> - for switches that implement global VLAN filtering, upper VLAN interfaces
> on top of standalone ports would require programming FDB and MDB entries
> with the appropriate VLAN ID, however there is no such tracking today
> AFAICT, so we are not yet solving those use cases yet, right?

Yes, here we're entering "brave" territory. With this patch set we have:

static inline bool dsa_switch_supports_uc_filtering(struct dsa_switch *ds)
{
	return ds->ops->port_fdb_add && ds->ops->port_fdb_del &&
	       ds->fdb_isolation && !ds->vlan_filtering_is_global &&
	       !ds->needs_standalone_vlan_filtering;
}

so yes, the use cases you mention are disqualified. To remove that
restriction, every time our .ndo_vlan_rx_add_vid is called, we'd need to
walk over our address lists (kept by struct net_device :: uc and mc),
and replay them in the newly added VLAN. This is the easy part; the
harder part is that we'd need to keep one more list of standalone VLANs
added on a port, in order to do the reverse - every time dsa_slave_sync_uc()
is called, we program that address not only in VID 0, but also in all
VIDs from the list of standalone VLANs on that port. It might be
possible to use vlan_for_each() for this. In any case it is incremental
complexity which I've not yet added.

This solution would install more host FDB entries than needed - the
Cartesian product of RX VLANs and secondary MAC addresses. I'm ok with
that, but there's also Ivan Khoronzhuk's patch series for Independent
Virtual Device Filtering (IVDF), which basically annotates each MAC
address from struct net_device with a VID. That one really seems
excessive, at this stage, to me.

> - what if the switch does not support FDB/MDB isolation, what would be our
> options here? As you might remember from a few months ago, the Broadcom
> roboswitch do not have any isolation, but what they can do is internally tag
> Ethernet frames with two VLAN tags, an that may be used as a form of
> isolation

The central thing I had in mind is that DSA doesn't really enforce that
packets are sent as 'control traffic'. The users of tag_8021q are the
closest example to me - routing towards the correct egress port is done
through a VID added to the packet, later stripped. The essential
characteristic of a packet sent by a DSA tagger as a 'data packet' is
that the FDB lookup is unavoidable.

The reason why I added FDB isolation as a requirement is this:

   +--------------------+
   |                    |
   | +------+ +------+  |
   | | swp0 | | swp1 |  |
   +-+------+-+------+--+
        |        |
        +--------+

swp0 has 192.168.100.1/24, swp3 has 192.168.100.2/24, they are in
different net namespaces or vrfs and want to ping each other.

Without FDB isolation, the mantra is that DSA will keep the FDB devoid
of swp0's and swp1's MAC addresses. Flooding is enabled on all ports and
the CPU port, and learning is disabled.

With FDB isolation and this patch series, DSA will install the MAC
address of swp0 on the CPU port in swp0's private database, and the
address of swp1 on the CPU port in swp1's private database. It is the
driver's responsibility to then distinguish between those 2 databases
the best it can. VID, FID, EFID, you name it.

When swp0 sends a packet to swp1, it has swp0's MAC SA and swp1's MAC DA.
If DSA doesn't know in the general sense that there will be no FDB lookup,
then the best it can do is to ensure that swp0's and swp1's address are
at least not in the same database. Because otherwise, the CPU port will
see a packet with swp1's MAC DA, but the destination for that address is
the CPU port => drop.

Sadly I don't know if this scenario is hypothetical or not.
With sja1105, it wasn't until I converted it to isolate the FDBs
(packets are sent as 'data traffic', but every standalone port has a
different pvid, and packets sent from the CPU follow the egress port's
pvid). If there are more which I haven't caught (there are many DSA
drivers which I don't know very well), this would break them.
So I wanted to be on the conservative side, but I suppose I could be
convinced otherwise.

Please note that there is hope. On ocelot, standalone ports share the
same pvid of 0, and yet it still declares FDB isolation. This is because
traffic sent over the NPI port (using the "ocelot" tagger) bypasses the
FDB lookup and it will always reach its intended destination. And using
the "ocelot-8021q" tagger, packets hit a VCAP IS2 TCAM rule on the
ingress of the CPU port that redirects them to the desired egress port,
before the packet processing reaches the VLAN table.

As for the Roboswitch discussion:
https://lore.kernel.org/all/04d700b6-a4f7-b108-e711-d400ef01cc2e@bang-olufsen.dk/T/#mb2c185003d8a58c691918e056cf09fd0ef6d1bd0

that was mainly about TX forwarding offload, i.o.w. "how to use the FDB
lookup of data plane packets to your benefit, instead of against you".
In this context of FDB isolation I think it would be more beneficial to
you to keep sending control packets, if those bypass the FDB.

It is the bridge TX forwarding offload that I don't see how you could do.

My takeaway from that discussion is that the IMP port processes the VID
from the packet if VLAN awareness is turned on globally, and probably
reverts to a single default VID otherwise.

So even though you could arrange for each VLAN-unaware bridge to have a
driver-private port pvid, and your tagger could wrap packets in an outer
VLAN tag with this value, the fundamental problem seems to be that VLAN
awareness is global in the first place. So either your user ports can
offload a VLAN-unaware bridge, case in which the IMP can't see the VID
from the packets, or the IMP can see the VID from the packets, but you
can't offload a VLAN-unaware bridge.

In this case and if I have understood all the information, I think what
you can do is deny multiple bridges (regardless of VLAN awareness),
configure standalone ports to use pvid 0 or 4095 (and different from the
pvid of VLAN-unaware bridge ports) and just declare that you support FDB
isolation.
Alvin Šipraga March 3, 2022, 12:16 p.m. UTC | #3
Hi Vladimir,

Vladimir Oltean <vladimir.oltean@nxp.com> writes:

> This series doesn't attempt anything extremely brave, it just changes
> the way in which standalone ports which support FDB isolation work.
>
> Up until now, DSA has recommended that switch drivers configure
> standalone ports in a separate VID/FID with learning disabled, and with
> the CPU port as the only destination, reached trivially via flooding.
> That works, except that standalone ports will deliver all packets to the
> CPU. We can leverage the hardware FDB as a MAC DA filter, and disable
> flooding towards the CPU port, to force the dropping of packets with
> unknown MAC DA.
>
> We handle port promiscuity by re-enabling flooding towards the CPU port.
> This is relevant because the bridge puts its automatic (learning +
> flooding) ports in promiscuous mode, and this makes some things work
> automagically, like for example bridging with a foreign interface.
> We don't delve yet into the territory of managing CPU flooding more
> aggressively while under a bridge.
>
> The only switch driver that benefits from this work right now is the
> NXP LS1028A switch (felix). The others need to implement FDB isolation
> first, before DSA is going to install entries to the port's standalone
> database. Otherwise, these entries might collide with bridge FDB/MDB
> entries.
>
> This work was done mainly to have all the required features in place
> before somebody starts seriously architecting DSA support for multiple
> CPU ports. Otherwise it is much more difficult to bolt these features on
> top of multiple CPU ports.

So, previously FDB entries were only installed on bridged ports. Now you
also want to install FDB entries on standalone ports so that flooding
can be disabled on standalone ports for the reasons stated in your cover
letter.

To implement FDB isolation in a DSA driver, a typical approach might be
to use a filter ID (FID) for the FDB entries that is unique per
bridge. That is, since FDB entries were only added on bridged ports
(through learning or static entries added by software), the DSA driver
could readily use the bridge_num of the bridge that is being offloaded
to select the FID. The same bridge_num/FID would be used by the hardware
for lookup/learning on the given port.

If the above general statements are correct-ish, then my question here
is: what should be the FID - or other equivalent unique identifier used
by the hardware for FDB isolation - when the port is not offloading a
bridge, but is standalone? If FDB isolation is implemented in hardware
with something like FIDs, then do all standalone ports need to have a
unique FID?

For some context: I have been working on implementing offload features
for the rtl8365mb driver and I can also support FDB isolation between
bridged ports. The number of offloaded bridges is bounded by the number
of FIDs available, which is 8. For standalone ports I use a reserved
FID=0 which currently would never match any entries in the FDB, because
learning is disabled on standalone ports and Linux does not install any
FDB entries. When placed in a bridge, the FID of that port is then set
to bridge_num, which - rather conveniently - is indexed by 1.

Your change seems to introduce a more generic concept of per-port
FDB. How should one model the per-port FDB in hardware which uses FIDs?
Should I ensure that all ports - standalone by default - start with a
unique FID? That will be OK for switches with up to 8 ports, but for
switches with more ports, I'm a but puzzled as to what I can do. Do I
then have to declare that FDB isolation is unsupported
(fdb_isolation=0)?

Hope the question makes sense.

Kind regards,
Alvin
Vladimir Oltean March 3, 2022, 1:18 p.m. UTC | #4
Hi Alvin,

On Thu, Mar 03, 2022 at 12:16:26PM +0000, Alvin Šipraga wrote:
> Hi Vladimir,
> 
> Vladimir Oltean <vladimir.oltean@nxp.com> writes:
> 
> > This series doesn't attempt anything extremely brave, it just changes
> > the way in which standalone ports which support FDB isolation work.
> >
> > Up until now, DSA has recommended that switch drivers configure
> > standalone ports in a separate VID/FID with learning disabled, and with
> > the CPU port as the only destination, reached trivially via flooding.
> > That works, except that standalone ports will deliver all packets to the
> > CPU. We can leverage the hardware FDB as a MAC DA filter, and disable
> > flooding towards the CPU port, to force the dropping of packets with
> > unknown MAC DA.
> >
> > We handle port promiscuity by re-enabling flooding towards the CPU port.
> > This is relevant because the bridge puts its automatic (learning +
> > flooding) ports in promiscuous mode, and this makes some things work
> > automagically, like for example bridging with a foreign interface.
> > We don't delve yet into the territory of managing CPU flooding more
> > aggressively while under a bridge.
> >
> > The only switch driver that benefits from this work right now is the
> > NXP LS1028A switch (felix). The others need to implement FDB isolation
> > first, before DSA is going to install entries to the port's standalone
> > database. Otherwise, these entries might collide with bridge FDB/MDB
> > entries.
> >
> > This work was done mainly to have all the required features in place
> > before somebody starts seriously architecting DSA support for multiple
> > CPU ports. Otherwise it is much more difficult to bolt these features on
> > top of multiple CPU ports.
> 
> So, previously FDB entries were only installed on bridged ports. Now you
> also want to install FDB entries on standalone ports so that flooding
> can be disabled on standalone ports for the reasons stated in your cover
> letter.

Not "on standalone ports", but on the CPU ports, for the standalone
ports' addresses. Otherwise, yes.

> To implement FDB isolation in a DSA driver, a typical approach might be
> to use a filter ID (FID) for the FDB entries that is unique per
> bridge. That is, since FDB entries were only added on bridged ports
> (through learning or static entries added by software), the DSA driver
> could readily use the bridge_num of the bridge that is being offloaded
> to select the FID. The same bridge_num/FID would be used by the hardware
> for lookup/learning on the given port.

Yes and no. "FID" is a double-edged sword, it will actually depend upon
hardware implementation. FID in general is a mechanism for FDB
partitioning. If the VID->FID translation is keyed only by VID, then the
only intended use case for that is to support shared VLAN learning,
where all VIDs use the same FID (=> the same database for addresses).
This isn't very interesting for us.
If the VID->FID translation is keyed by {port, VID}, it is then possible
to make VIDs on different ports (part of different bridges) use
different FIDs, and that is what is interesting.

> If the above general statements are correct-ish, then my question here
> is: what should be the FID - or other equivalent unique identifier used
> by the hardware for FDB isolation - when the port is not offloading a
> bridge, but is standalone? If FDB isolation is implemented in hardware
> with something like FIDs, then do all standalone ports need to have a
> unique FID?

Not necessarily, although as far as the DSA core is concerned, we treat
drivers supporting FDB isolation as though each port has its own database.
For example, the sja1105 driver really has unique VIDs (=> FIDs) per
standalone port, so if we didn't treat it that way, it wouldn't work.

It is important to visualize that there are only 2 directions for a
packet to travel in a standalone port's FID: either from the line side
to the CPU, or from the CPU to the line side.

If the "CPU to line side" direction is unimpeded by FDB lookups, then
only the direction towards the CPU matters.

Further assuming that there is a single CPU port (you didn't ask about
multiple CPU ports, so I won't tell), the only practical implication of
standalone ports sharing the same FID is that they will, in effect,
share the same MAC DA filters. So, if you have 4 slave interfaces, each
with its own unicast and multicast address lists, what will happen is
that each port will accept the union of all others too. It's not
perfect, in the sense that unnecessary packets would still reach the
CPU, but they would still be dropped there.
For the felix/ocelot driver, I was ok with that => that driver uses the
same VID/FID of 0 for all standalone ports.

> For some context: I have been working on implementing offload features
> for the rtl8365mb driver and I can also support FDB isolation between
> bridged ports. The number of offloaded bridges is bounded by the number
> of FIDs available, which is 8. For standalone ports I use a reserved
> FID=0 which currently would never match any entries in the FDB, because
> learning is disabled on standalone ports and Linux does not install any
> FDB entries. When placed in a bridge, the FID of that port is then set
> to bridge_num, which - rather conveniently - is indexed by 1.
> 
> Your change seems to introduce a more generic concept of per-port
> FDB. How should one model the per-port FDB in hardware which uses FIDs?
> Should I ensure that all ports - standalone by default - start with a
> unique FID? That will be OK for switches with up to 8 ports, but for
> switches with more ports, I'm a but puzzled as to what I can do. Do I
> then have to declare that FDB isolation is unsupported
> (fdb_isolation=0)?
> 
> Hope the question makes sense.

As long as
(a) tag_rtl8_4.c sends packets to standalone ports in a way in which FDB
    lookups are bypassed
(a) you're not concerned about the hardware MAC DA filters being a bit
    more relaxed than software expects
then I wouldn't worry too much about it, and keep using FID 0 for all
standalone ports.
patchwork-bot+netdevbpf@kernel.org March 3, 2022, 2:20 p.m. UTC | #5
Hello:

This series was applied to netdev/net-next.git (master)
by David S. Miller <davem@davemloft.net>:

On Wed,  2 Mar 2022 21:14:07 +0200 you wrote:
> This series doesn't attempt anything extremely brave, it just changes
> the way in which standalone ports which support FDB isolation work.
> 
> Up until now, DSA has recommended that switch drivers configure
> standalone ports in a separate VID/FID with learning disabled, and with
> the CPU port as the only destination, reached trivially via flooding.
> That works, except that standalone ports will deliver all packets to the
> CPU. We can leverage the hardware FDB as a MAC DA filter, and disable
> flooding towards the CPU port, to force the dropping of packets with
> unknown MAC DA.
> 
> [...]

Here is the summary with links:
  - [net-next,01/10] net: dsa: remove workarounds for changing master promisc/allmulti only while up
    https://git.kernel.org/netdev/net-next/c/35aae5ab9121
  - [net-next,02/10] net: dsa: rename the host FDB and MDB methods to contain the "bridge" namespace
    https://git.kernel.org/netdev/net-next/c/68d6d71eafd1
  - [net-next,03/10] net: dsa: install secondary unicast and multicast addresses as host FDB/MDB
    https://git.kernel.org/netdev/net-next/c/5e8a1e03aa4d
  - [net-next,04/10] net: dsa: install the primary unicast MAC address as standalone port host FDB
    https://git.kernel.org/netdev/net-next/c/499aa9e1b332
  - [net-next,05/10] net: dsa: manage flooding on the CPU ports
    https://git.kernel.org/netdev/net-next/c/7569459a52c9
  - [net-next,06/10] net: dsa: felix: migrate host FDB and MDB entries when changing tag proto
    https://git.kernel.org/netdev/net-next/c/f9cef64fa23f
  - [net-next,07/10] net: dsa: felix: migrate flood settings from NPI to tag_8021q CPU port
    https://git.kernel.org/netdev/net-next/c/b903a6bd2e19
  - [net-next,08/10] net: dsa: felix: start off with flooding disabled on the CPU port
    https://git.kernel.org/netdev/net-next/c/90897569beb1
  - [net-next,09/10] net: dsa: felix: stop clearing CPU flooding in felix_setup_tag_8021q
    https://git.kernel.org/netdev/net-next/c/0cc369800e5f
  - [net-next,10/10] net: mscc: ocelot: accept configuring bridge port flags on the NPI port
    https://git.kernel.org/netdev/net-next/c/ac4552096023

You are awesome, thank you!
Alvin Šipraga March 3, 2022, 2:20 p.m. UTC | #6
Vladimir Oltean <vladimir.oltean@nxp.com> writes:

> Hi Alvin,
>
> On Thu, Mar 03, 2022 at 12:16:26PM +0000, Alvin Šipraga wrote:
>> Hi Vladimir,
>> 
>> Vladimir Oltean <vladimir.oltean@nxp.com> writes:
>> 
>> > This series doesn't attempt anything extremely brave, it just changes
>> > the way in which standalone ports which support FDB isolation work.
>> >
>> > Up until now, DSA has recommended that switch drivers configure
>> > standalone ports in a separate VID/FID with learning disabled, and with
>> > the CPU port as the only destination, reached trivially via flooding.
>> > That works, except that standalone ports will deliver all packets to the
>> > CPU. We can leverage the hardware FDB as a MAC DA filter, and disable
>> > flooding towards the CPU port, to force the dropping of packets with
>> > unknown MAC DA.
>> >
>> > We handle port promiscuity by re-enabling flooding towards the CPU port.
>> > This is relevant because the bridge puts its automatic (learning +
>> > flooding) ports in promiscuous mode, and this makes some things work
>> > automagically, like for example bridging with a foreign interface.
>> > We don't delve yet into the territory of managing CPU flooding more
>> > aggressively while under a bridge.
>> >
>> > The only switch driver that benefits from this work right now is the
>> > NXP LS1028A switch (felix). The others need to implement FDB isolation
>> > first, before DSA is going to install entries to the port's standalone
>> > database. Otherwise, these entries might collide with bridge FDB/MDB
>> > entries.
>> >
>> > This work was done mainly to have all the required features in place
>> > before somebody starts seriously architecting DSA support for multiple
>> > CPU ports. Otherwise it is much more difficult to bolt these features on
>> > top of multiple CPU ports.
>> 
>> So, previously FDB entries were only installed on bridged ports. Now you
>> also want to install FDB entries on standalone ports so that flooding
>> can be disabled on standalone ports for the reasons stated in your cover
>> letter.
>
> Not "on standalone ports", but on the CPU ports, for the standalone
> ports' addresses. Otherwise, yes.
>
>> To implement FDB isolation in a DSA driver, a typical approach might be
>> to use a filter ID (FID) for the FDB entries that is unique per
>> bridge. That is, since FDB entries were only added on bridged ports
>> (through learning or static entries added by software), the DSA driver
>> could readily use the bridge_num of the bridge that is being offloaded
>> to select the FID. The same bridge_num/FID would be used by the hardware
>> for lookup/learning on the given port.
>
> Yes and no. "FID" is a double-edged sword, it will actually depend upon
> hardware implementation. FID in general is a mechanism for FDB
> partitioning. If the VID->FID translation is keyed only by VID, then the
> only intended use case for that is to support shared VLAN learning,
> where all VIDs use the same FID (=> the same database for addresses).
> This isn't very interesting for us.
> If the VID->FID translation is keyed by {port, VID}, it is then possible
> to make VIDs on different ports (part of different bridges) use
> different FIDs, and that is what is interesting.
>
>> If the above general statements are correct-ish, then my question here
>> is: what should be the FID - or other equivalent unique identifier used
>> by the hardware for FDB isolation - when the port is not offloading a
>> bridge, but is standalone? If FDB isolation is implemented in hardware
>> with something like FIDs, then do all standalone ports need to have a
>> unique FID?
>
> Not necessarily, although as far as the DSA core is concerned, we treat
> drivers supporting FDB isolation as though each port has its own database.
> For example, the sja1105 driver really has unique VIDs (=> FIDs) per
> standalone port, so if we didn't treat it that way, it wouldn't work.

I think I'm not quite grokking what it means for a port to have its own
database. Above you corrected me by stating that this patchset does not
install FDB entries "on standalone ports", but on the CPU ports. The way
I parse this is: you are adding an FDB entry to the database of the
CPU port. But how would that help with the "line side to CPU side"
direction? When a packet from the "line side" enters one of the
(standalone) user ports, the switch would appeal to the forwarding
database of that port, not the CPU port, no? So how does it help to
install FDB entries on the CPU port(s)?

Basically for a switch with two ports, swp0 and swp1, with MAC addresses
foo and bar respectively, we want the following FDB entries to be
installed when both ports are in standalone mode:

    MAC | VID | PORT
    ----+-----+-------------
    foo |  0  | cpu_port_num
    bar |  0  | cpu_port_num

Such that, when swp0 receives a packet with MAC DA 'foo', it knows to
forward that packet to cpu_port_num, i.e. towards the CPU port, instead
of flooding/dropping/trapping it. And ditto for swp2 and 'bar'. Right?

Now DSA assumes that each port has its own forwarding database, so let
me annotate how I see that by adding another column "PORT DB":

    MAC | VID | PORT         | PORT DB
    ----+-----+--------------+--------------
    foo |  0  | cpu_port_num | swp0_port_num  [*]
    bar |  0  | cpu_port_num | swp1_port_num  [**]

This codifies better that the entry for 'foo' should, ideally, only be
used by the switch when it receives a packet from 'foo' on swp0, but not
if it's received on swp1, etc. Internally this might be weakened to the
same underlying FID, but as you already pointed out, this is acceptable.

Overall this seems logical and OK to me, but I can't reconcile it with
your statement that the FDB entries in question are installed "on the
CPU port". Wouldn't that look like this?

    MAC | VID | PORT         | PORT DB
    ----+-----+--------------+-------------
    foo |  0  | cpu_port_num | cpu_port_num
    bar |  0  | cpu_port_num | cpu_port_num

... which seems wrong.

I'm suspecting/hoping that my above misunderstanding is just a matter
of terminology. For the sake of emphasis, allow me to describe in plain
English the FDB entries [*] and [**] above:

 [*] add an FDB entry on swp0 where [the host/station? with] MAC address
     'foo' is behind the CPU port on VID 0
[**] add an FDB entry on swp1 where [the host/station? with] MAC address
     'bar' is behind the CPU port on VID 0

Merely an attempt, and perhaps not idiomatic, but now you can tell me
what's wrong in describing it that way :-)

>
> It is important to visualize that there are only 2 directions for a
> packet to travel in a standalone port's FID: either from the line side
> to the CPU, or from the CPU to the line side.
>
> If the "CPU to line side" direction is unimpeded by FDB lookups, then
> only the direction towards the CPU matters.
>
> Further assuming that there is a single CPU port (you didn't ask about
> multiple CPU ports, so I won't tell), the only practical implication of
> standalone ports sharing the same FID is that they will, in effect,
> share the same MAC DA filters. So, if you have 4 slave interfaces, each
> with its own unicast and multicast address lists, what will happen is
> that each port will accept the union of all others too. It's not
> perfect, in the sense that unnecessary packets would still reach the
> CPU, but they would still be dropped there.

Right, this is the scenario I had in mind. Fine by me if this is
considered acceptable. I see how it is still better than flooding every
packet.

> For the felix/ocelot driver, I was ok with that => that driver uses the
> same VID/FID of 0 for all standalone ports.
>
>> For some context: I have been working on implementing offload features
>> for the rtl8365mb driver and I can also support FDB isolation between
>> bridged ports. The number of offloaded bridges is bounded by the number
>> of FIDs available, which is 8. For standalone ports I use a reserved
>> FID=0 which currently would never match any entries in the FDB, because
>> learning is disabled on standalone ports and Linux does not install any
>> FDB entries. When placed in a bridge, the FID of that port is then set
>> to bridge_num, which - rather conveniently - is indexed by 1.
>> 
>> Your change seems to introduce a more generic concept of per-port
>> FDB. How should one model the per-port FDB in hardware which uses FIDs?
>> Should I ensure that all ports - standalone by default - start with a
>> unique FID? That will be OK for switches with up to 8 ports, but for
>> switches with more ports, I'm a but puzzled as to what I can do. Do I
>> then have to declare that FDB isolation is unsupported
>> (fdb_isolation=0)?
>> 
>> Hope the question makes sense.
>
> As long as
> (a) tag_rtl8_4.c sends packets to standalone ports in a way in which FDB
>     lookups are bypassed

Yes, it does.

> (a) you're not concerned about the hardware MAC DA filters being a bit
>     more relaxed than software expects

Since they are dropped in software, it doesn't matter to me.

> then I wouldn't worry too much about it, and keep using FID 0 for all
> standalone ports.

Great, thanks a lot for clarifying.

Kind regards,
Alvin
Vladimir Oltean March 3, 2022, 2:35 p.m. UTC | #7
On Thu, Mar 03, 2022 at 02:20:29PM +0000, Alvin Šipraga wrote:
> Vladimir Oltean <vladimir.oltean@nxp.com> writes:
> 
> > Hi Alvin,
> >
> > On Thu, Mar 03, 2022 at 12:16:26PM +0000, Alvin Šipraga wrote:
> >> Hi Vladimir,
> >> 
> >> Vladimir Oltean <vladimir.oltean@nxp.com> writes:
> >> 
> >> > This series doesn't attempt anything extremely brave, it just changes
> >> > the way in which standalone ports which support FDB isolation work.
> >> >
> >> > Up until now, DSA has recommended that switch drivers configure
> >> > standalone ports in a separate VID/FID with learning disabled, and with
> >> > the CPU port as the only destination, reached trivially via flooding.
> >> > That works, except that standalone ports will deliver all packets to the
> >> > CPU. We can leverage the hardware FDB as a MAC DA filter, and disable
> >> > flooding towards the CPU port, to force the dropping of packets with
> >> > unknown MAC DA.
> >> >
> >> > We handle port promiscuity by re-enabling flooding towards the CPU port.
> >> > This is relevant because the bridge puts its automatic (learning +
> >> > flooding) ports in promiscuous mode, and this makes some things work
> >> > automagically, like for example bridging with a foreign interface.
> >> > We don't delve yet into the territory of managing CPU flooding more
> >> > aggressively while under a bridge.
> >> >
> >> > The only switch driver that benefits from this work right now is the
> >> > NXP LS1028A switch (felix). The others need to implement FDB isolation
> >> > first, before DSA is going to install entries to the port's standalone
> >> > database. Otherwise, these entries might collide with bridge FDB/MDB
> >> > entries.
> >> >
> >> > This work was done mainly to have all the required features in place
> >> > before somebody starts seriously architecting DSA support for multiple
> >> > CPU ports. Otherwise it is much more difficult to bolt these features on
> >> > top of multiple CPU ports.
> >> 
> >> So, previously FDB entries were only installed on bridged ports. Now you
> >> also want to install FDB entries on standalone ports so that flooding
> >> can be disabled on standalone ports for the reasons stated in your cover
> >> letter.
> >
> > Not "on standalone ports", but on the CPU ports, for the standalone
> > ports' addresses. Otherwise, yes.
> >
> >> To implement FDB isolation in a DSA driver, a typical approach might be
> >> to use a filter ID (FID) for the FDB entries that is unique per
> >> bridge. That is, since FDB entries were only added on bridged ports
> >> (through learning or static entries added by software), the DSA driver
> >> could readily use the bridge_num of the bridge that is being offloaded
> >> to select the FID. The same bridge_num/FID would be used by the hardware
> >> for lookup/learning on the given port.
> >
> > Yes and no. "FID" is a double-edged sword, it will actually depend upon
> > hardware implementation. FID in general is a mechanism for FDB
> > partitioning. If the VID->FID translation is keyed only by VID, then the
> > only intended use case for that is to support shared VLAN learning,
> > where all VIDs use the same FID (=> the same database for addresses).
> > This isn't very interesting for us.
> > If the VID->FID translation is keyed by {port, VID}, it is then possible
> > to make VIDs on different ports (part of different bridges) use
> > different FIDs, and that is what is interesting.
> >
> >> If the above general statements are correct-ish, then my question here
> >> is: what should be the FID - or other equivalent unique identifier used
> >> by the hardware for FDB isolation - when the port is not offloading a
> >> bridge, but is standalone? If FDB isolation is implemented in hardware
> >> with something like FIDs, then do all standalone ports need to have a
> >> unique FID?
> >
> > Not necessarily, although as far as the DSA core is concerned, we treat
> > drivers supporting FDB isolation as though each port has its own database.
> > For example, the sja1105 driver really has unique VIDs (=> FIDs) per
> > standalone port, so if we didn't treat it that way, it wouldn't work.
> 
> I think I'm not quite grokking what it means for a port to have its own
> database. Above you corrected me by stating that this patchset does not
> install FDB entries "on standalone ports", but on the CPU ports. The way
> I parse this is: you are adding an FDB entry to the database of the
> CPU port.

No. I am adding an FDB entry _towards_ the CPU port.

Perhaps I need to clarify what I mean by a "database". It is a construct
used by DSA to denote "which subset of ports should match this entry".
The entries from the database of a port should only be matched by that
port when it operates as standalone. Not by other ports, not by bridged
ports.
The entries from the database of a bridge should only be matched by the
ports in that bridge. Not by ports in other bridges, not by standalone
ports.
The entries from the database of a LAG should only be matched by the
ports in that LAG.

The FDB entries added here belong to the database of a standalone port.
This is necessary, because classifying the packet to a FID is done on
the ingress (=> standalone) port. But the destination is the CPU port.

All shared (DSA and CPU) ports serve multiple databases.

> But how would that help with the "line side to CPU side"
> direction? When a packet from the "line side" enters one of the
> (standalone) user ports, the switch would appeal to the forwarding
> database of that port, not the CPU port, no? So how does it help to
> install FDB entries on the CPU port(s)?

See above. This is exactly why we pass the struct dsa_db as argument to
.port_fdb_add(), and why you can't just deduce the database from the
provided "port" argument. The CPU port must serve the databases of
standalone ports.

> Basically for a switch with two ports, swp0 and swp1, with MAC addresses
> foo and bar respectively, we want the following FDB entries to be
> installed when both ports are in standalone mode:
> 
>     MAC | VID | PORT
>     ----+-----+-------------
>     foo |  0  | cpu_port_num
>     bar |  0  | cpu_port_num
> 
> Such that, when swp0 receives a packet with MAC DA 'foo', it knows to
> forward that packet to cpu_port_num, i.e. towards the CPU port, instead
> of flooding/dropping/trapping it. And ditto for swp2 and 'bar'. Right?
> 
> Now DSA assumes that each port has its own forwarding database, so let
> me annotate how I see that by adding another column "PORT DB":
> 
>     MAC | VID | PORT         | PORT DB
>     ----+-----+--------------+--------------
>     foo |  0  | cpu_port_num | swp0_port_num  [*]
>     bar |  0  | cpu_port_num | swp1_port_num  [**]
> 
> This codifies better that the entry for 'foo' should, ideally, only be
> used by the switch when it receives a packet from 'foo' on swp0, but not
> if it's received on swp1, etc. Internally this might be weakened to the
> same underlying FID, but as you already pointed out, this is acceptable.
> 
> Overall this seems logical and OK to me, but I can't reconcile it with
> your statement that the FDB entries in question are installed "on the
> CPU port". Wouldn't that look like this?
> 
>     MAC | VID | PORT         | PORT DB
>     ----+-----+--------------+-------------
>     foo |  0  | cpu_port_num | cpu_port_num
>     bar |  0  | cpu_port_num | cpu_port_num
> 
> ... which seems wrong.

See above. Basically, when I say "install a FDB entry on port X", I mean
"install it and make it point towards port X". Maybe I should be more
specific.

> I'm suspecting/hoping that my above misunderstanding is just a matter
> of terminology. For the sake of emphasis, allow me to describe in plain
> English the FDB entries [*] and [**] above:
> 
>  [*] add an FDB entry on swp0 where [the host/station? with] MAC address
>      'foo' is behind the CPU port on VID 0
> [**] add an FDB entry on swp1 where [the host/station? with] MAC address
>      'bar' is behind the CPU port on VID 0
> 
> Merely an attempt, and perhaps not idiomatic, but now you can tell me
> what's wrong in describing it that way :-)

Perhaps:

[*]  add an FDB entry to the switch, with MAC address 'foo', which can
     only be matched for packets ingressing swp0 in standalone mode
     (i.e. is in swp0's port database), whose destination is the CPU
     port

[**] same thing, just replace 'foo' with 'bar', and 'swp0' with 'swp1'
Vladimir Oltean March 3, 2022, 2:48 p.m. UTC | #8
On Thu, Mar 03, 2022 at 04:35:29PM +0200, Vladimir Oltean wrote:
> On Thu, Mar 03, 2022 at 02:20:29PM +0000, Alvin Šipraga wrote:
> > Vladimir Oltean <vladimir.oltean@nxp.com> writes:
> > 
> > > Hi Alvin,
> > >
> > > On Thu, Mar 03, 2022 at 12:16:26PM +0000, Alvin Šipraga wrote:
> > >> Hi Vladimir,
> > >> 
> > >> Vladimir Oltean <vladimir.oltean@nxp.com> writes:
> > >> 
> > >> > This series doesn't attempt anything extremely brave, it just changes
> > >> > the way in which standalone ports which support FDB isolation work.
> > >> >
> > >> > Up until now, DSA has recommended that switch drivers configure
> > >> > standalone ports in a separate VID/FID with learning disabled, and with
> > >> > the CPU port as the only destination, reached trivially via flooding.
> > >> > That works, except that standalone ports will deliver all packets to the
> > >> > CPU. We can leverage the hardware FDB as a MAC DA filter, and disable
> > >> > flooding towards the CPU port, to force the dropping of packets with
> > >> > unknown MAC DA.
> > >> >
> > >> > We handle port promiscuity by re-enabling flooding towards the CPU port.
> > >> > This is relevant because the bridge puts its automatic (learning +
> > >> > flooding) ports in promiscuous mode, and this makes some things work
> > >> > automagically, like for example bridging with a foreign interface.
> > >> > We don't delve yet into the territory of managing CPU flooding more
> > >> > aggressively while under a bridge.
> > >> >
> > >> > The only switch driver that benefits from this work right now is the
> > >> > NXP LS1028A switch (felix). The others need to implement FDB isolation
> > >> > first, before DSA is going to install entries to the port's standalone
> > >> > database. Otherwise, these entries might collide with bridge FDB/MDB
> > >> > entries.
> > >> >
> > >> > This work was done mainly to have all the required features in place
> > >> > before somebody starts seriously architecting DSA support for multiple
> > >> > CPU ports. Otherwise it is much more difficult to bolt these features on
> > >> > top of multiple CPU ports.
> > >> 
> > >> So, previously FDB entries were only installed on bridged ports. Now you
> > >> also want to install FDB entries on standalone ports so that flooding
> > >> can be disabled on standalone ports for the reasons stated in your cover
> > >> letter.
> > >
> > > Not "on standalone ports", but on the CPU ports, for the standalone
> > > ports' addresses. Otherwise, yes.
> > >
> > >> To implement FDB isolation in a DSA driver, a typical approach might be
> > >> to use a filter ID (FID) for the FDB entries that is unique per
> > >> bridge. That is, since FDB entries were only added on bridged ports
> > >> (through learning or static entries added by software), the DSA driver
> > >> could readily use the bridge_num of the bridge that is being offloaded
> > >> to select the FID. The same bridge_num/FID would be used by the hardware
> > >> for lookup/learning on the given port.
> > >
> > > Yes and no. "FID" is a double-edged sword, it will actually depend upon
> > > hardware implementation. FID in general is a mechanism for FDB
> > > partitioning. If the VID->FID translation is keyed only by VID, then the
> > > only intended use case for that is to support shared VLAN learning,
> > > where all VIDs use the same FID (=> the same database for addresses).
> > > This isn't very interesting for us.
> > > If the VID->FID translation is keyed by {port, VID}, it is then possible
> > > to make VIDs on different ports (part of different bridges) use
> > > different FIDs, and that is what is interesting.
> > >
> > >> If the above general statements are correct-ish, then my question here
> > >> is: what should be the FID - or other equivalent unique identifier used
> > >> by the hardware for FDB isolation - when the port is not offloading a
> > >> bridge, but is standalone? If FDB isolation is implemented in hardware
> > >> with something like FIDs, then do all standalone ports need to have a
> > >> unique FID?
> > >
> > > Not necessarily, although as far as the DSA core is concerned, we treat
> > > drivers supporting FDB isolation as though each port has its own database.
> > > For example, the sja1105 driver really has unique VIDs (=> FIDs) per
> > > standalone port, so if we didn't treat it that way, it wouldn't work.
> > 
> > I think I'm not quite grokking what it means for a port to have its own
> > database. Above you corrected me by stating that this patchset does not
> > install FDB entries "on standalone ports", but on the CPU ports. The way
> > I parse this is: you are adding an FDB entry to the database of the
> > CPU port.
> 
> No. I am adding an FDB entry _towards_ the CPU port.
> 
> Perhaps I need to clarify what I mean by a "database". It is a construct
> used by DSA to denote "which subset of ports should match this entry".
> The entries from the database of a port should only be matched by that
> port when it operates as standalone. Not by other ports, not by bridged
> ports.
> The entries from the database of a bridge should only be matched by the
> ports in that bridge. Not by ports in other bridges, not by standalone
> ports.
> The entries from the database of a LAG should only be matched by the
> ports in that LAG.
> 
> The FDB entries added here belong to the database of a standalone port.
> This is necessary, because classifying the packet to a FID is done on
> the ingress (=> standalone) port. But the destination is the CPU port.
> 
> All shared (DSA and CPU) ports serve multiple databases.
> 
> > But how would that help with the "line side to CPU side"
> > direction? When a packet from the "line side" enters one of the
> > (standalone) user ports, the switch would appeal to the forwarding
> > database of that port, not the CPU port, no? So how does it help to
> > install FDB entries on the CPU port(s)?
> 
> See above. This is exactly why we pass the struct dsa_db as argument to
> .port_fdb_add(), and why you can't just deduce the database from the
> provided "port" argument. The CPU port must serve the databases of
> standalone ports.
> 
> > Basically for a switch with two ports, swp0 and swp1, with MAC addresses
> > foo and bar respectively, we want the following FDB entries to be
> > installed when both ports are in standalone mode:
> > 
> >     MAC | VID | PORT
> >     ----+-----+-------------
> >     foo |  0  | cpu_port_num
> >     bar |  0  | cpu_port_num
> > 
> > Such that, when swp0 receives a packet with MAC DA 'foo', it knows to
> > forward that packet to cpu_port_num, i.e. towards the CPU port, instead
> > of flooding/dropping/trapping it. And ditto for swp2 and 'bar'. Right?
> > 
> > Now DSA assumes that each port has its own forwarding database, so let
> > me annotate how I see that by adding another column "PORT DB":
> > 
> >     MAC | VID | PORT         | PORT DB
> >     ----+-----+--------------+--------------
> >     foo |  0  | cpu_port_num | swp0_port_num  [*]
> >     bar |  0  | cpu_port_num | swp1_port_num  [**]
> > 
> > This codifies better that the entry for 'foo' should, ideally, only be
> > used by the switch when it receives a packet from 'foo' on swp0, but not
> > if it's received on swp1, etc. Internally this might be weakened to the
> > same underlying FID, but as you already pointed out, this is acceptable.
> > 
> > Overall this seems logical and OK to me, but I can't reconcile it with
> > your statement that the FDB entries in question are installed "on the
> > CPU port". Wouldn't that look like this?
> > 
> >     MAC | VID | PORT         | PORT DB
> >     ----+-----+--------------+-------------
> >     foo |  0  | cpu_port_num | cpu_port_num
> >     bar |  0  | cpu_port_num | cpu_port_num
> > 
> > ... which seems wrong.
> 
> See above. Basically, when I say "install a FDB entry on port X", I mean
> "install it and make it point towards port X". Maybe I should be more
> specific.
> 
> > I'm suspecting/hoping that my above misunderstanding is just a matter
> > of terminology. For the sake of emphasis, allow me to describe in plain
> > English the FDB entries [*] and [**] above:
> > 
> >  [*] add an FDB entry on swp0 where [the host/station? with] MAC address
> >      'foo' is behind the CPU port on VID 0
> > [**] add an FDB entry on swp1 where [the host/station? with] MAC address
> >      'bar' is behind the CPU port on VID 0
> > 
> > Merely an attempt, and perhaps not idiomatic, but now you can tell me
> > what's wrong in describing it that way :-)
> 
> Perhaps:
> 
> [*]  add an FDB entry to the switch, with MAC address 'foo', which can
>      only be matched for packets ingressing swp0 in standalone mode
>      (i.e. is in swp0's port database), whose destination is the CPU
>      port
> 
> [**] same thing, just replace 'foo' with 'bar', and 'swp0' with 'swp1'

Let me give you a concrete example.

The way in which sja1105 uses tag_8021q is that (assuming 4 ports), the
following are configured as PVIDs in standalone mode:

swp0 has PVID 3072 and MAC address 00:01:02:03:04:00
swp1 has PVID 3073 and MAC address 00:01:02:03:04:01
swp2 has PVID 3074 and MAC address 00:01:02:03:04:02
swp3 has PVID 3075 and MAC address 00:01:02:03:04:03
Port 4 is the CPU port. It doesn't have a PVID - all traffic ingressing
it is VLAN-tagged, what isn't tagged is dropped.

When DSA wants to program the unicast address of swp0 to hardware for
unicast filtering, it calls .port_fdb_add(port 4, address 00:01:02:03:04:00,
VID 0, DSA_DB_PORT with a "dp" of swp0).
The sja1105 driver then translates the database of swp0 to VID/FID 3072,
and installs MAC 00:01:02:03:04:00 & VID 3072 as a FDB entry towards port 4.

This is what we want, because all packets received on swp0 in standalone
mode are classified by the hardware to VID/FID 3072. They will therefore
match the FDB entry and be routed to the CPU port. No other port will
match on that entry, because every other port has a different PVID.

DSA has no underlying knowledge of how the drivers handle their FIDs, it
just passes the information they need such that they can make the
necessary translation when installing FDB entries.
Alvin Šipraga March 3, 2022, 3:13 p.m. UTC | #9
Vladimir Oltean <vladimir.oltean@nxp.com> writes:

> On Thu, Mar 03, 2022 at 02:20:29PM +0000, Alvin Šipraga wrote:
>> Vladimir Oltean <vladimir.oltean@nxp.com> writes:
>> 
>> > Hi Alvin,
>> >
>> > On Thu, Mar 03, 2022 at 12:16:26PM +0000, Alvin Šipraga wrote:
>> >> Hi Vladimir,
>> >> 
>> >> Vladimir Oltean <vladimir.oltean@nxp.com> writes:
>> >> 
>> >> > This series doesn't attempt anything extremely brave, it just changes
>> >> > the way in which standalone ports which support FDB isolation work.
>> >> >
>> >> > Up until now, DSA has recommended that switch drivers configure
>> >> > standalone ports in a separate VID/FID with learning disabled, and with
>> >> > the CPU port as the only destination, reached trivially via flooding.
>> >> > That works, except that standalone ports will deliver all packets to the
>> >> > CPU. We can leverage the hardware FDB as a MAC DA filter, and disable
>> >> > flooding towards the CPU port, to force the dropping of packets with
>> >> > unknown MAC DA.
>> >> >
>> >> > We handle port promiscuity by re-enabling flooding towards the CPU port.
>> >> > This is relevant because the bridge puts its automatic (learning +
>> >> > flooding) ports in promiscuous mode, and this makes some things work
>> >> > automagically, like for example bridging with a foreign interface.
>> >> > We don't delve yet into the territory of managing CPU flooding more
>> >> > aggressively while under a bridge.
>> >> >
>> >> > The only switch driver that benefits from this work right now is the
>> >> > NXP LS1028A switch (felix). The others need to implement FDB isolation
>> >> > first, before DSA is going to install entries to the port's standalone
>> >> > database. Otherwise, these entries might collide with bridge FDB/MDB
>> >> > entries.
>> >> >
>> >> > This work was done mainly to have all the required features in place
>> >> > before somebody starts seriously architecting DSA support for multiple
>> >> > CPU ports. Otherwise it is much more difficult to bolt these features on
>> >> > top of multiple CPU ports.
>> >> 
>> >> So, previously FDB entries were only installed on bridged ports. Now you
>> >> also want to install FDB entries on standalone ports so that flooding
>> >> can be disabled on standalone ports for the reasons stated in your cover
>> >> letter.
>> >
>> > Not "on standalone ports", but on the CPU ports, for the standalone
>> > ports' addresses. Otherwise, yes.
>> >
>> >> To implement FDB isolation in a DSA driver, a typical approach might be
>> >> to use a filter ID (FID) for the FDB entries that is unique per
>> >> bridge. That is, since FDB entries were only added on bridged ports
>> >> (through learning or static entries added by software), the DSA driver
>> >> could readily use the bridge_num of the bridge that is being offloaded
>> >> to select the FID. The same bridge_num/FID would be used by the hardware
>> >> for lookup/learning on the given port.
>> >
>> > Yes and no. "FID" is a double-edged sword, it will actually depend upon
>> > hardware implementation. FID in general is a mechanism for FDB
>> > partitioning. If the VID->FID translation is keyed only by VID, then the
>> > only intended use case for that is to support shared VLAN learning,
>> > where all VIDs use the same FID (=> the same database for addresses).
>> > This isn't very interesting for us.
>> > If the VID->FID translation is keyed by {port, VID}, it is then possible
>> > to make VIDs on different ports (part of different bridges) use
>> > different FIDs, and that is what is interesting.
>> >
>> >> If the above general statements are correct-ish, then my question here
>> >> is: what should be the FID - or other equivalent unique identifier used
>> >> by the hardware for FDB isolation - when the port is not offloading a
>> >> bridge, but is standalone? If FDB isolation is implemented in hardware
>> >> with something like FIDs, then do all standalone ports need to have a
>> >> unique FID?
>> >
>> > Not necessarily, although as far as the DSA core is concerned, we treat
>> > drivers supporting FDB isolation as though each port has its own database.
>> > For example, the sja1105 driver really has unique VIDs (=> FIDs) per
>> > standalone port, so if we didn't treat it that way, it wouldn't work.
>> 
>> I think I'm not quite grokking what it means for a port to have its own
>> database. Above you corrected me by stating that this patchset does not
>> install FDB entries "on standalone ports", but on the CPU ports. The way
>> I parse this is: you are adding an FDB entry to the database of the
>> CPU port.
>
> No. I am adding an FDB entry _towards_ the CPU port.
>
> Perhaps I need to clarify what I mean by a "database". It is a construct
> used by DSA to denote "which subset of ports should match this entry".
> The entries from the database of a port should only be matched by that
> port when it operates as standalone. Not by other ports, not by bridged
> ports.
> The entries from the database of a bridge should only be matched by the
> ports in that bridge. Not by ports in other bridges, not by standalone
> ports.
> The entries from the database of a LAG should only be matched by the
> ports in that LAG.
>
> The FDB entries added here belong to the database of a standalone port.
> This is necessary, because classifying the packet to a FID is done on
> the ingress (=> standalone) port. But the destination is the CPU port.
>
> All shared (DSA and CPU) ports serve multiple databases.
>
>> But how would that help with the "line side to CPU side"
>> direction? When a packet from the "line side" enters one of the
>> (standalone) user ports, the switch would appeal to the forwarding
>> database of that port, not the CPU port, no? So how does it help to
>> install FDB entries on the CPU port(s)?
>
> See above. This is exactly why we pass the struct dsa_db as argument to
> .port_fdb_add(), and why you can't just deduce the database from the
> provided "port" argument. The CPU port must serve the databases of
> standalone ports.

Right, this was what was I was missing. Now I follow.

It helped to take another look at how sja1105 handles it:

static int sja1105_fdb_add(struct dsa_switch *ds, int port,
			   const unsigned char *addr, u16 vid,
			   struct dsa_db db)
{
	struct sja1105_private *priv = ds->priv;

	if (!vid) {
		switch (db.type) {
		case DSA_DB_PORT:
			vid = dsa_tag_8021q_standalone_vid(db.dp);
			break;
		case DSA_DB_BRIDGE:
			vid = dsa_tag_8021q_bridge_vid(db.bridge.num);
			break;
		default:
			return -EOPNOTSUPP;
		}
	}

	return priv->info->fdb_add_cmd(ds, port, addr, vid);
}

So, yes, we do call .port_fdb_add() "on" the CPU port (i.e. the 'port'
argument refers to the port number of the CPU port). But this just means
telling the switch to forward packets with MAC DA 'addr' to 'port'. The
actual forwarding database - (standalone) port, or bridge, or LAG
database - is determined by the 'db' parameter. It is up to the DSA
driver to make sense of the info in db.{dp,bridge,lag} in order to
ensure proper FDB isolation, such that the entry is only matched (on
packet ingress) by the ports implied by the given database (a single
standalone port, or a group of ports belonging to the same bridge, or a
group of ports in a LAG).

>
>> Basically for a switch with two ports, swp0 and swp1, with MAC addresses
>> foo and bar respectively, we want the following FDB entries to be
>> installed when both ports are in standalone mode:
>> 
>>     MAC | VID | PORT
>>     ----+-----+-------------
>>     foo |  0  | cpu_port_num
>>     bar |  0  | cpu_port_num
>> 
>> Such that, when swp0 receives a packet with MAC DA 'foo', it knows to
>> forward that packet to cpu_port_num, i.e. towards the CPU port, instead
>> of flooding/dropping/trapping it. And ditto for swp2 and 'bar'. Right?
>> 
>> Now DSA assumes that each port has its own forwarding database, so let
>> me annotate how I see that by adding another column "PORT DB":
>> 
>>     MAC | VID | PORT         | PORT DB
>>     ----+-----+--------------+--------------
>>     foo |  0  | cpu_port_num | swp0_port_num  [*]
>>     bar |  0  | cpu_port_num | swp1_port_num  [**]
>> 
>> This codifies better that the entry for 'foo' should, ideally, only be
>> used by the switch when it receives a packet from 'foo' on swp0, but not
>> if it's received on swp1, etc. Internally this might be weakened to the
>> same underlying FID, but as you already pointed out, this is acceptable.
>> 
>> Overall this seems logical and OK to me, but I can't reconcile it with
>> your statement that the FDB entries in question are installed "on the
>> CPU port". Wouldn't that look like this?
>> 
>>     MAC | VID | PORT         | PORT DB
>>     ----+-----+--------------+-------------
>>     foo |  0  | cpu_port_num | cpu_port_num
>>     bar |  0  | cpu_port_num | cpu_port_num
>> 
>> ... which seems wrong.
>
> See above. Basically, when I say "install a FDB entry on port X", I mean
> "install it and make it point towards port X". Maybe I should be more
> specific.
>
>> I'm suspecting/hoping that my above misunderstanding is just a matter
>> of terminology. For the sake of emphasis, allow me to describe in plain
>> English the FDB entries [*] and [**] above:
>> 
>>  [*] add an FDB entry on swp0 where [the host/station? with] MAC address
>>      'foo' is behind the CPU port on VID 0
>> [**] add an FDB entry on swp1 where [the host/station? with] MAC address
>>      'bar' is behind the CPU port on VID 0
>> 
>> Merely an attempt, and perhaps not idiomatic, but now you can tell me
>> what's wrong in describing it that way :-)
>
> Perhaps:
>
> [*]  add an FDB entry to the switch, with MAC address 'foo', which can
>      only be matched for packets ingressing swp0 in standalone mode
>      (i.e. is in swp0's port database), whose destination is the CPU
>      port
>
> [**] same thing, just replace 'foo' with 'bar', and 'swp0' with 'swp1'

Superb, thanks as always for the fine explanation.

Kind regards,
Alvin
Vladimir Oltean March 3, 2022, 3:35 p.m. UTC | #10
On Thu, Mar 03, 2022 at 03:13:41PM +0000, Alvin Šipraga wrote:
> So, yes, we do call .port_fdb_add() "on" the CPU port (i.e. the 'port'
> argument refers to the port number of the CPU port). But this just means
> telling the switch to forward packets with MAC DA 'addr' to 'port'. The
> actual forwarding database - (standalone) port, or bridge, or LAG
> database - is determined by the 'db' parameter. It is up to the DSA
> driver to make sense of the info in db.{dp,bridge,lag} in order to
> ensure proper FDB isolation, such that the entry is only matched (on
> packet ingress) by the ports implied by the given database (a single
> standalone port, or a group of ports belonging to the same bridge, or a
> group of ports in a LAG).

This is indeed what I was trying to say.