From: Vladimir Oltean <olteanv@gmail.com> Date: 2021-02-06 02:40:06
From: Vladimir Oltean <vladimir.oltean@nxp.com>
This patch series reworks the ocelot switchdev driver such that it could
share the same implementation for LAG offload as the felix DSA driver.
Testing has been done in the following topology:
+----------------------------------+
| Board 1 br0 |
| +---------+ |
| / \ |
| | | |
| | bond0 |
| | +-----+ |
| | / \ |
| eno0 swp0 swp1 swp2 |
+---|--------|-------|-------|-----+
| | | |
+--------+ | |
Cable | |
Cable| |Cable
Cable | |
+--------+ | |
| | | |
+---|--------|-------|-------|-----+
| eno0 swp0 swp1 swp2 |
| | \ / |
| | +-----+ |
| | bond0 |
| | | |
| \ / |
| +---------+ |
| Board 2 br0 |
+----------------------------------+
The same script can be run on both Board 1 and Board 2 to set this up:
ip link del bond0
ip link add bond0 type bond mode balance-xor miimon 1
OR
ip link add bond0 type bond mode 802.3ad
ip link set swp1 down && ip link set swp1 master bond0 && ip link set swp1 up
ip link set swp2 down && ip link set swp2 master bond0 && ip link set swp2 up
ip link del br0
ip link add br0 type bridge
ip link set bond0 master br0
ip link set swp0 master br0
Then traffic can be tested between eno0 of Board 1 and eno0 of Board 2.
Vladimir Oltean (12):
net: mscc: ocelot: rename ocelot_netdevice_port_event to
ocelot_netdevice_changeupper
net: mscc: ocelot: use a switch-case statement in
ocelot_netdevice_event
net: mscc: ocelot: don't refuse bonding interfaces we can't offload
net: mscc: ocelot: use ipv6 in the aggregation code
net: mscc: ocelot: set up the bonding mask in a way that avoids a
net_device
net: mscc: ocelot: avoid unneeded "lp" variable in LAG join
net: mscc: ocelot: set up logical port IDs centrally
net: mscc: ocelot: drop the use of the "lags" array
net: mscc: ocelot: rename aggr_count to num_ports_in_lag
net: mscc: ocelot: rebalance LAGs on link up/down events
net: dsa: make assisted_learning_on_cpu_port bypass offloaded LAG
interfaces
net: dsa: felix: propagate the LAG offload ops towards the ocelot lib
drivers/net/dsa/ocelot/felix.c | 32 ++++
drivers/net/ethernet/mscc/ocelot.c | 206 ++++++++++++++-----------
drivers/net/ethernet/mscc/ocelot.h | 4 -
drivers/net/ethernet/mscc/ocelot_net.c | 131 ++++++++++------
include/soc/mscc/ocelot.h | 11 +-
net/dsa/dsa_priv.h | 13 ++
net/dsa/slave.c | 8 +
7 files changed, 262 insertions(+), 143 deletions(-)
--
2.25.1
From: Vladimir Oltean <olteanv@gmail.com> Date: 2021-02-06 02:33:15
From: Vladimir Oltean <vladimir.oltean@nxp.com>
At present there is an issue when ocelot is offloading a bonding
interface, but one of the links of the physical ports goes down. Traffic
keeps being hashed towards that destination, and of course gets dropped
on egress.
Monitor the netdev notifier events emitted by the bonding driver for
changes in the physical state of lower interfaces, to determine which
ports are active and which ones are no longer.
Then extend ocelot_get_bond_mask to return either the configured bonding
interfaces, or the active ones, depending on a boolean argument. The
code that does rebalancing only needs to do so among the active ports,
whereas the bridge forwarding mask and the logical port IDs still need
to look at the permanently bonded ports.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Changes in v3:
- Return a proper notifier error code in
ocelot_netdevice_changelowerstate.
- Export ocelot_port_lag_change.
- Adapt to changes in ocelot_apply_bridge_fwd_mask.
Changes in v2:
- Adapt to the merged version of the DSA API, which now passes just a
bool lag_tx_active in .port_lag_change instead of the full struct
netdev_lag_lower_state_info *info.
- Renamed "just_active_ports" -> "only_active_ports"
drivers/net/ethernet/mscc/ocelot.c | 41 ++++++++++++++++++++------
drivers/net/ethernet/mscc/ocelot.h | 1 +
drivers/net/ethernet/mscc/ocelot_net.c | 30 +++++++++++++++++++
include/soc/mscc/ocelot.h | 1 +
4 files changed, 64 insertions(+), 9 deletions(-)
@@ -889,7 +889,8 @@ int ocelot_get_ts_info(struct ocelot *ocelot, int port,}EXPORT_SYMBOL(ocelot_get_ts_info);-staticu32ocelot_get_bond_mask(structocelot*ocelot,structnet_device*bond)+staticu32ocelot_get_bond_mask(structocelot*ocelot,structnet_device*bond,+boolonly_active_ports){u32mask=0;intport;
@@ -960,8 +965,10 @@ void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot)structnet_device*bond=ocelot_port->bond;mask=ocelot->bridge_fwd_mask&~BIT(port);-if(bond)-mask&=~ocelot_get_bond_mask(ocelot,bond);+if(bond){+mask&=~ocelot_get_bond_mask(ocelot,bond,+false);+}}else{/* Standalone ports forward only to DSA tag_8021q CPU*ports(ifthoseexist),ortothehardwareCPUport
@@ -1298,20 +1305,20 @@ static void ocelot_set_aggr_pgids(struct ocelot *ocelot)/* Now, set PGIDs for each active LAG */for(lag=0;lag<ocelot->num_phys_ports;lag++){structnet_device*bond=ocelot->ports[lag]->bond;-intnum_ports_in_lag=0;+intnum_active_ports=0;unsignedlongbond_mask;u8aggr_idx[16];if(!bond||(visited&BIT(lag)))continue;-bond_mask=ocelot_get_bond_mask(ocelot,bond);+bond_mask=ocelot_get_bond_mask(ocelot,bond,true);for_each_set_bit(port,&bond_mask,ocelot->num_phys_ports){// Destination maskocelot_write_rix(ocelot,bond_mask,ANA_PGID_PGID,port);-aggr_idx[num_ports_in_lag++]=port;+aggr_idx[num_active_ports++]=port;}for_each_aggr_pgid(ocelot,i){
@@ -1319,7 +1326,11 @@ static void ocelot_set_aggr_pgids(struct ocelot *ocelot)ac=ocelot_read_rix(ocelot,ANA_PGID_PGID,i);ac&=~bond_mask;-ac|=BIT(aggr_idx[i%num_ports_in_lag]);+/* Don't do division by zero if there was no active+*port.Justmakeallaggregationcodeszero.+*/+if(num_active_ports)+ac|=BIT(aggr_idx[i%num_active_ports]);ocelot_write_rix(ocelot,ac,ANA_PGID_PGID,i);}
@@ -1399,6 +1411,17 @@ void ocelot_port_lag_leave(struct ocelot *ocelot, int port,}EXPORT_SYMBOL(ocelot_port_lag_leave);+voidocelot_port_lag_change(structocelot*ocelot,intport,boollag_tx_active)+{+structocelot_port*ocelot_port=ocelot->ports[port];++ocelot_port->lag_tx_active=lag_tx_active;++/* Rebalance the LAGs */+ocelot_set_aggr_pgids(ocelot);+}+EXPORT_SYMBOL(ocelot_port_lag_change);+/* Configure the maximum SDU (L2 payload) on RX to the value specified in @sdu.*ThelengthofVLANtagsisaccountedforautomaticallyviaDEV_MAC_TAGS_CFG.*Inthespecialcasethatit'stheNPIportthatwe'reconfiguring,the
@@ -114,6 +114,7 @@ int ocelot_port_lag_join(struct ocelot *ocelot, int port,structnetdev_lag_upper_info*info);voidocelot_port_lag_leave(structocelot*ocelot,intport,structnet_device*bond);+voidocelot_port_lag_change(structocelot*ocelot,intport,boollag_tx_active);structnet_device*ocelot_port_to_netdev(structocelot*ocelot,intport);intocelot_netdev_to_port(structnet_device*dev);
From: Vladimir Oltean <olteanv@gmail.com> Date: 2021-02-06 02:37:38
From: Vladimir Oltean <vladimir.oltean@nxp.com>
The ocelot switch has been supporting LAG offload since its initial
commit, however felix could not make use of that, due to lack of a LAG
abstraction in DSA. Now that we have that, let's forward DSA's calls
towards the ocelot library, who will deal with setting up the bonding.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Changes in v3:
lag_leave and lag_change now return void.
Changes in v2:
s/lag_dev/bond/g
drivers/net/dsa/ocelot/felix.c | 32 ++++++++++++++++++++++++++++++
drivers/net/ethernet/mscc/ocelot.h | 6 ------
include/soc/mscc/ocelot.h | 6 ++++++
3 files changed, 38 insertions(+), 6 deletions(-)
@@ -109,12 +109,6 @@ int ocelot_mact_learn(struct ocelot *ocelot, int port,unsignedintvid,enummacaccess_entry_typetype);intocelot_mact_forget(structocelot*ocelot,constunsignedcharmac[ETH_ALEN],unsignedintvid);-intocelot_port_lag_join(structocelot*ocelot,intport,-structnet_device*bond,-structnetdev_lag_upper_info*info);-voidocelot_port_lag_leave(structocelot*ocelot,intport,-structnet_device*bond);-voidocelot_port_lag_change(structocelot*ocelot,intport,boollag_tx_active);structnet_device*ocelot_port_to_netdev(structocelot*ocelot,intport);intocelot_netdev_to_port(structnet_device*dev);
@@ -798,6 +798,12 @@ int ocelot_port_mdb_add(struct ocelot *ocelot, int port,conststructswitchdev_obj_port_mdb*mdb);intocelot_port_mdb_del(structocelot*ocelot,intport,conststructswitchdev_obj_port_mdb*mdb);+intocelot_port_lag_join(structocelot*ocelot,intport,+structnet_device*bond,+structnetdev_lag_upper_info*info);+voidocelot_port_lag_leave(structocelot*ocelot,intport,+structnet_device*bond);+voidocelot_port_lag_change(structocelot*ocelot,intport,boollag_tx_active);intocelot_devlink_sb_register(structocelot*ocelot);voidocelot_devlink_sb_unregister(structocelot*ocelot);
From: Vladimir Oltean <olteanv@gmail.com> Date: 2021-02-06 02:38:51
From: Vladimir Oltean <vladimir.oltean@nxp.com>
Since this code should be called from pure switchdev as well as from
DSA, we must find a way to determine the bonding mask not by looking
directly at the net_device lowers of the bonding interface, since those
could have different private structures.
We keep a pointer to the bonding upper interface, if present, in struct
ocelot_port. Then the bonding mask becomes the bitwise OR of all ports
that have the same bonding upper interface. This adds a duplication of
functionality with the current "lags" array, but the duplication will be
short-lived, since further patches will remove the latter completely.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
---
Changes in v3:
None.
Changes in v2:
Adapted to the merged version of the DSA API for LAG offload (i.e.
rejecting a bonding interface due to tx_type now done within the
.port_lag_join callback, caller is supposed to handle -EOPNOTSUPP).
drivers/net/ethernet/mscc/ocelot.c | 29 ++++++++++++++++++++++-------
include/soc/mscc/ocelot.h | 2 ++
2 files changed, 24 insertions(+), 7 deletions(-)
@@ -889,6 +889,24 @@ int ocelot_get_ts_info(struct ocelot *ocelot, int port,}EXPORT_SYMBOL(ocelot_get_ts_info);+staticu32ocelot_get_bond_mask(structocelot*ocelot,structnet_device*bond)+{+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->bond==bond)+mask|=BIT(port);+}++returnmask;+}+staticu32ocelot_get_dsa_8021q_cpu_mask(structocelot*ocelot){u32mask=0;
@@ -1319,20 +1337,15 @@ int ocelot_port_lag_join(struct ocelot *ocelot, int port,structnet_device*bond,structnetdev_lag_upper_info*info){-structnet_device*ndev;u32bond_mask=0;intlag,lp;if(info->tx_type!=NETDEV_LAG_TX_TYPE_HASH)return-EOPNOTSUPP;-rcu_read_lock();-for_each_netdev_in_bond_rcu(bond,ndev){-structocelot_port_private*priv=netdev_priv(ndev);+ocelot->ports[port]->bond=bond;-bond_mask|=BIT(priv->chip_port);-}-rcu_read_unlock();+bond_mask=ocelot_get_bond_mask(ocelot,bond);lp=__ffs(bond_mask);
@@ -1366,6 +1379,8 @@ void ocelot_port_lag_leave(struct ocelot *ocelot, int port,u32port_cfg;inti;+ocelot->ports[port]->bond=NULL;+/* Remove port from any lag */for(i=0;i<ocelot->num_phys_ports;i++)ocelot->lags[i]&=~BIT(port);
From: Vladimir Oltean <olteanv@gmail.com> Date: 2021-02-06 02:43:16
From: Vladimir Oltean <vladimir.oltean@nxp.com>
Make ocelot's net device event handler more streamlined by structuring
it in a similar way with others. The inspiration here was
dsa_slave_netdevice_event.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
---
drivers/net/ethernet/mscc/ocelot_net.c | 68 +++++++++++++++++---------
1 file changed, 45 insertions(+), 23 deletions(-)
From: Vladimir Oltean <olteanv@gmail.com> Date: 2021-02-06 02:47:14
From: Vladimir Oltean <vladimir.oltean@nxp.com>
It makes it a bit easier to read and understand the code that deals with
balancing the 16 aggregation codes among the ports in a certain LAG.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Changes in v3:
None.
Changes in v2:
None.
drivers/net/ethernet/mscc/ocelot.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
@@ -1298,8 +1298,8 @@ static void ocelot_set_aggr_pgids(struct ocelot *ocelot)/* Now, set PGIDs for each active LAG */for(lag=0;lag<ocelot->num_phys_ports;lag++){structnet_device*bond=ocelot->ports[lag]->bond;+intnum_ports_in_lag=0;unsignedlongbond_mask;-intaggr_count=0;u8aggr_idx[16];if(!bond||(visited&BIT(lag)))
From: Vladimir Oltean <olteanv@gmail.com> Date: 2021-02-06 02:51:57
From: Vladimir Oltean <vladimir.oltean@nxp.com>
The setup of logical port IDs is done in two places: from the inconclusively
named ocelot_setup_lag and from ocelot_port_lag_leave, a function that
also calls ocelot_setup_lag (which apparently does an incomplete setup
of the LAG).
To improve this situation, we can rename ocelot_setup_lag into
ocelot_setup_logical_port_ids, and drop the "lag" argument. It will now
set up the logical port IDs of all switch ports, which may be just
slightly more inefficient but more maintainable.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Changes in v3:
None.
Changes in v2:
None.
drivers/net/ethernet/mscc/ocelot.c | 47 ++++++++++++++++++------------
1 file changed, 28 insertions(+), 19 deletions(-)
@@ -1316,20 +1316,36 @@ static void ocelot_set_aggr_pgids(struct ocelot *ocelot)}}-staticvoidocelot_setup_lag(structocelot*ocelot,intlag)+/* When offloading a bonding interface, the switch ports configured under the+*samebondmusthavethesamelogicalportID,equaltothephysicalportID+*ofthelowestnumberedphysicalportinthatbond.Otherwise,instandalone/+*bridgedmode,eachporthasalogicalportIDequaltoitsphysicalportID.+*/+staticvoidocelot_setup_logical_port_ids(structocelot*ocelot){-unsignedlongbond_mask=ocelot->lags[lag];-unsignedintp;+intport;-for_each_set_bit(p,&bond_mask,ocelot->num_phys_ports){-u32port_cfg=ocelot_read_gix(ocelot,ANA_PORT_PORT_CFG,p);+for(port=0;port<ocelot->num_phys_ports;port++){+structocelot_port*ocelot_port=ocelot->ports[port];+structnet_device*bond;++if(!ocelot_port)+continue;-port_cfg&=~ANA_PORT_PORT_CFG_PORTID_VAL_M;+bond=ocelot_port->bond;+if(bond){+intlag=__ffs(ocelot_get_bond_mask(ocelot,bond));-/* Use lag port as logical port for port i */-ocelot_write_gix(ocelot,port_cfg|-ANA_PORT_PORT_CFG_PORTID_VAL(lag),-ANA_PORT_PORT_CFG,p);+ocelot_rmw_gix(ocelot,+ANA_PORT_PORT_CFG_PORTID_VAL(lag),+ANA_PORT_PORT_CFG_PORTID_VAL_M,+ANA_PORT_PORT_CFG,port);+}else{+ocelot_rmw_gix(ocelot,+ANA_PORT_PORT_CFG_PORTID_VAL(port),+ANA_PORT_PORT_CFG_PORTID_VAL_M,+ANA_PORT_PORT_CFG,port);+}}}
@@ -1361,7 +1377,7 @@ int ocelot_port_lag_join(struct ocelot *ocelot, int port,ocelot->lags[lag]|=BIT(port);}-ocelot_setup_lag(ocelot,lag);+ocelot_setup_logical_port_ids(ocelot);ocelot_apply_bridge_fwd_mask(ocelot);ocelot_set_aggr_pgids(ocelot);
From: Vladimir Oltean <olteanv@gmail.com> Date: 2021-02-06 02:57:39
From: Vladimir Oltean <vladimir.oltean@nxp.com>
We can now simplify the implementation by always using ocelot_get_bond_mask
to look up the other ports that are offloading the same bonding interface
as us.
In ocelot_set_aggr_pgids, the code had a way to uniquely iterate through
LAGs. We need to achieve the same behavior by marking each LAG as visited,
which we do now by using a temporary 32-bit "visited" bitmask. This is
ok and we do not need dynamic memory allocation, because we know that
this switch architecture will not have more than 32 ports (the PGID port
masks are 32-bit anyway).
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Changes in v3:
Using a "visited" bit mask to avoid memory allocation.
Changes in v2:
Context looks a bit different.
drivers/net/ethernet/mscc/ocelot.c | 95 ++++++++++++------------------
include/soc/mscc/ocelot.h | 2 -
2 files changed, 39 insertions(+), 58 deletions(-)
@@ -1287,16 +1278,35 @@ static void ocelot_set_aggr_pgids(struct ocelot *ocelot)ocelot_write_rix(ocelot,GENMASK(ocelot->num_phys_ports-1,0),ANA_PGID_PGID,i);-/* Now, set PGIDs for each LAG */+/* The visited ports bitmask holds the list of ports offloading any+*bondinginterface.Initiallywemarkalltheseportsasunvisited,+*theneverytimewevisitaportinthisbitmask,weknowthatitis+*thelowestnumberedport,i.e.theonewhoselogicalID==physical+*portID==LAGID.Sowemarkasvisitedallfurtherportsinthe+*bitmaskthatareoffloadingthesamebondinginterface.Thisway,+*wesetuptheaggregationPGIDsonlyonceperbondinginterface.+*/+for(port=0;port<ocelot->num_phys_ports;port++){+structocelot_port*ocelot_port=ocelot->ports[port];++if(!ocelot_port||!ocelot_port->bond)+continue;++visited&=~BIT(port);+}++/* Now, set PGIDs for each active LAG */for(lag=0;lag<ocelot->num_phys_ports;lag++){+structnet_device*bond=ocelot->ports[lag]->bond;unsignedlongbond_mask;intaggr_count=0;u8aggr_idx[16];-bond_mask=ocelot->lags[lag];-if(!bond_mask)+if(!bond||(visited&BIT(lag)))continue;+bond_mask=ocelot_get_bond_mask(ocelot,bond);+for_each_set_bit(port,&bond_mask,ocelot->num_phys_ports){// Destination maskocelot_write_rix(ocelot,bond_mask,
@@ -1313,6 +1323,19 @@ static void ocelot_set_aggr_pgids(struct ocelot *ocelot)ac|=BIT(aggr_idx[i%aggr_count]);ocelot_write_rix(ocelot,ac,ANA_PGID_PGID,i);}++/* Mark all ports in the same LAG as visited to avoid applying+*thesameconfigagain.+*/+for(port=lag;port<ocelot->num_phys_ports;port++){+structocelot_port*ocelot_port=ocelot->ports[port];++if(!ocelot_port)+continue;++if(ocelot_port->bond==bond)+visited|=BIT(port);+}}}
@@ -1353,30 +1376,11 @@ int ocelot_port_lag_join(struct ocelot *ocelot, int port,structnet_device*bond,structnetdev_lag_upper_info*info){-u32bond_mask=0;-intlag;-if(info->tx_type!=NETDEV_LAG_TX_TYPE_HASH)return-EOPNOTSUPP;ocelot->ports[port]->bond=bond;-bond_mask=ocelot_get_bond_mask(ocelot,bond);--lag=__ffs(bond_mask);--/* If the new port is the lowest one, use it as the logical port from-*nowon-*/-if(port==lag){-ocelot->lags[port]=bond_mask;-bond_mask&=~BIT(port);-if(bond_mask)-ocelot->lags[__ffs(bond_mask)]=0;-}else{-ocelot->lags[lag]|=BIT(port);-}-ocelot_setup_logical_port_ids(ocelot);ocelot_apply_bridge_fwd_mask(ocelot);ocelot_set_aggr_pgids(ocelot);
@@ -1388,24 +1392,8 @@ EXPORT_SYMBOL(ocelot_port_lag_join);voidocelot_port_lag_leave(structocelot*ocelot,intport,structnet_device*bond){-inti;-ocelot->ports[port]->bond=NULL;-/* Remove port from any lag */-for(i=0;i<ocelot->num_phys_ports;i++)-ocelot->lags[i]&=~BIT(port);--/* if it was the logical port of the lag, move the lag config to the-*nextport-*/-if(ocelot->lags[port]){-intn=__ffs(ocelot->lags[port]);--ocelot->lags[n]=ocelot->lags[port];-ocelot->lags[port]=0;-}-ocelot_setup_logical_port_ids(ocelot);ocelot_apply_bridge_fwd_mask(ocelot);ocelot_set_aggr_pgids(ocelot);
@@ -1587,11 +1575,6 @@ int ocelot_init(struct ocelot *ocelot)}}-ocelot->lags=devm_kcalloc(ocelot->dev,ocelot->num_phys_ports,-sizeof(u32),GFP_KERNEL);-if(!ocelot->lags)-return-ENOMEM;-ocelot->stats=devm_kcalloc(ocelot->dev,ocelot->num_phys_ports*ocelot->num_stats,sizeof(u64),GFP_KERNEL);
From: Vladimir Oltean <olteanv@gmail.com> Date: 2021-02-06 03:01:48
From: Vladimir Oltean <vladimir.oltean@nxp.com>
The index of the LAG is equal to the logical port ID that all the
physical port members have, which is further equal to the index of the
first physical port that is a member of the LAG.
The code gets a bit carried away with logic like this:
if (a == b)
c = a;
else
c = b;
which can be simplified, of course, into:
c = b;
(with a being port, b being lp, c being lag)
This further makes the "lp" variable redundant, since we can use "lag"
everywhere where "lp" (logical port) was used. So instead of a "c = b"
assignment, we can do a complete deletion of b. Only one comment here:
if (bond_mask) {
lp = __ffs(bond_mask);
ocelot->lags[lp] = 0;
}
lp was clobbered before, because it was used as a temporary variable to
hold the new smallest port ID from the bond. Now that we don't have "lp"
any longer, we'll just avoid the temporary variable and zeroize the
bonding mask directly.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
---
Changes in v3:
None.
Changes in v2:
None.
drivers/net/ethernet/mscc/ocelot.c | 16 ++++++----------
1 file changed, 6 insertions(+), 10 deletions(-)
@@ -1338,7 +1338,7 @@ int ocelot_port_lag_join(struct ocelot *ocelot, int port,structnetdev_lag_upper_info*info){u32bond_mask=0;-intlag,lp;+intlag;if(info->tx_type!=NETDEV_LAG_TX_TYPE_HASH)return-EOPNOTSUPP;
@@ -1347,22 +1347,18 @@ int ocelot_port_lag_join(struct ocelot *ocelot, int port,bond_mask=ocelot_get_bond_mask(ocelot,bond);-lp=__ffs(bond_mask);+lag=__ffs(bond_mask);/* If the new port is the lowest one, use it as the logical port from*nowon*/-if(port==lp){-lag=port;+if(port==lag){ocelot->lags[port]=bond_mask;bond_mask&=~BIT(port);-if(bond_mask){-lp=__ffs(bond_mask);-ocelot->lags[lp]=0;-}+if(bond_mask)+ocelot->lags[__ffs(bond_mask)]=0;}else{-lag=lp;-ocelot->lags[lp]|=BIT(port);+ocelot->lags[lag]|=BIT(port);}ocelot_setup_lag(ocelot,lag);
From: Vladimir Oltean <olteanv@gmail.com> Date: 2021-02-06 03:13:10
From: Vladimir Oltean <vladimir.oltean@nxp.com>
Since switchdev/DSA exposes network interfaces that fulfill many of the
same user space expectations that dedicated NICs do, it makes sense to
not deny bonding interfaces with a bonding policy that we cannot offload,
but instead allow the bonding driver to select the egress interface in
software.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
---
Changes in v3:
None.
Changes in v2:
Addressed Alex's feedback:
This changes the return value in case of error, I'm not sure how
important this is.
by keeping the return code of notifier_from_errno(-EINVAL)
drivers/net/ethernet/mscc/ocelot.c | 6 ++++-
drivers/net/ethernet/mscc/ocelot.h | 3 ++-
drivers/net/ethernet/mscc/ocelot_net.c | 36 +++++++-------------------
3 files changed, 17 insertions(+), 28 deletions(-)
@@ -110,7 +110,8 @@ int ocelot_mact_learn(struct ocelot *ocelot, int port,intocelot_mact_forget(structocelot*ocelot,constunsignedcharmac[ETH_ALEN],unsignedintvid);intocelot_port_lag_join(structocelot*ocelot,intport,-structnet_device*bond);+structnet_device*bond,+structnetdev_lag_upper_info*info);voidocelot_port_lag_leave(structocelot*ocelot,intport,structnet_device*bond);structnet_device*ocelot_port_to_netdev(structocelot*ocelot,intport);
From: Vladimir Oltean <olteanv@gmail.com> Date: 2021-02-06 03:15:11
From: Vladimir Oltean <vladimir.oltean@nxp.com>
ocelot_netdevice_port_event treats a single event, NETDEV_CHANGEUPPER.
So we can remove the check for the type of event, and rename the
function to be more suggestive, since there already is a function with a
very similar name of ocelot_netdevice_event.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Changes in v3:
None.
Changes in v2:
Fixed build by removing the "event" argument of ocelot_netdevice_changeupper.
drivers/net/ethernet/mscc/ocelot_net.c | 59 ++++++++++++--------------
1 file changed, 27 insertions(+), 32 deletions(-)
From: Vladimir Oltean <olteanv@gmail.com> Date: 2021-02-06 03:17:03
From: Vladimir Oltean <vladimir.oltean@nxp.com>
Given the following topology, and focusing only on Box A:
Box A
+----------------------------------+
| Board 1 br0 |
| +---------+ |
| / \ |
| | | |
| | bond0 |
| | +-----+ |
|192.168.1.1 | / \ |
| eno0 swp0 swp1 swp2 |
+---|--------|-------|-------|-----+
| | | |
+--------+ | |
Cable | |
Cable| |Cable
Cable | |
+--------+ | |
| | | |
+---|--------|-------|-------|-----+
| eno0 swp0 swp1 swp2 |
|192.168.1.2 | \ / |
| | +-----+ |
| | bond0 |
| | | |
| \ / |
| +---------+ |
| Board 2 br0 |
+----------------------------------+
Box B
The assisted_learning_on_cpu_port logic will see that swp0 is bridged
with a "foreign interface" (bond0) and will therefore install all
addresses learnt by the software bridge towards bond0 (including the
address of eno0 on Box B) as static addresses towards the CPU port.
But that's not what we want - bond0 is not really a "foreign interface"
but one we can offload including L2 forwarding from/towards it. So we
need to refine our logic for assisted learning such that, whenever we
see an address learnt on a non-DSA interface, we search through the tree
for any port that offloads that non-DSA interface.
Some confusion might arise as to why we search through the whole tree
instead of just the local switch returned by dsa_slave_dev_lower_find.
Or a different angle of the same confusion: why does
dsa_slave_dev_lower_find(br_dev) return a single dp that's under br_dev
instead of the whole list of bridged DSA ports?
To answer the second question, it should be enough to install the static
FDB entry on the CPU port of a single switch in the tree, because
dsa_port_fdb_add uses DSA_NOTIFIER_FDB_ADD which ensures that all other
switches in the tree get notified of that address, and add the entry
themselves using dsa_towards_port().
This should help understand the answer to the first question: the port
returned by dsa_slave_dev_lower_find may not be on the same switch as
the ports that offload the LAG. Nonetheless, if the driver implements
.crosschip_lag_join and .crosschip_bridge_join as mv88e6xxx does, there
still isn't any reason for trapping addresses learnt on the remote LAG
towards the CPU, and we should prevent that.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Changes in v3:
Patch is new and necessary due to the recent introduction of assisted
learning on the CPU port.
net/dsa/dsa_priv.h | 13 +++++++++++++
net/dsa/slave.c | 8 ++++++++
2 files changed, 21 insertions(+)
@@ -209,6 +209,19 @@ static inline bool dsa_port_offloads_netdev(struct dsa_port *dp,returnfalse;}+/* Returns true if any port of this tree offloads the given net_device */+staticinlinebooldsa_tree_offloads_netdev(structdsa_switch_tree*dst,+structnet_device*dev)+{+structdsa_port*dp;++list_for_each_entry(dp,&dst->ports,list)+if(dsa_port_offloads_netdev(dp,dev))+returntrue;++returnfalse;+}+/* slave.c */externconststructdsa_device_opsnotag_netdev_ops;voiddsa_slave_mii_bus_init(structdsa_switch*ds);
@@ -2215,6 +2215,14 @@ static int dsa_slave_switchdev_event(struct notifier_block *unused,if(!dp->ds->assisted_learning_on_cpu_port)returnNOTIFY_DONE;++/* When the bridge learns an address on an offloaded+*LAGwedon'twanttosendtraffictotheCPU,the+*otherportsbridgedwiththeLAGshouldbeableto+*autonomouslyforwardtowardsit.+*/+if(dsa_tree_offloads_netdev(dp->ds->dst,dev))+returnNOTIFY_DONE;}if(!dp->ds->ops->port_fdb_add||!dp->ds->ops->port_fdb_del)
From: Vladimir Oltean <olteanv@gmail.com> Date: 2021-02-06 03:26:56
From: Vladimir Oltean <vladimir.oltean@nxp.com>
IPv6 header information is not currently part of the entropy source for
the 4-bit aggregation code used for LAG offload, even though it could be.
The hardware reference manual says about these fields:
ANA::AGGR_CFG.AC_IP6_TCPUDP_PORT_ENA
Use IPv6 TCP/UDP port when calculating aggregation code. Configure
identically for all ports. Recommended value is 1.
ANA::AGGR_CFG.AC_IP6_FLOW_LBL_ENA
Use IPv6 flow label when calculating AC. Configure identically for all
ports. Recommended value is 1.
Integration with the xmit_hash_policy of the bonding interface is TBD.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
---
Changes in v3:
None.
Changes in v2:
None.
drivers/net/ethernet/mscc/ocelot.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
@@ -1615,7 +1615,10 @@ int ocelot_init(struct ocelot *ocelot)ocelot_write(ocelot,ANA_AGGR_CFG_AC_SMAC_ENA|ANA_AGGR_CFG_AC_DMAC_ENA|ANA_AGGR_CFG_AC_IP4_SIPDIP_ENA|-ANA_AGGR_CFG_AC_IP4_TCPUDP_ENA,ANA_AGGR_CFG);+ANA_AGGR_CFG_AC_IP4_TCPUDP_ENA|+ANA_AGGR_CFG_AC_IP6_FLOW_LBL_ENA|+ANA_AGGR_CFG_AC_IP6_TCPUDP_ENA,+ANA_AGGR_CFG);/* Set MAC age time to default value. The entry is aged after*2*AGE_PERIOD
Hello:
This series was applied to netdev/net-next.git (refs/heads/master):
On Sat, 6 Feb 2021 00:02:09 +0200 you wrote:
From: Vladimir Oltean <vladimir.oltean@nxp.com>
This patch series reworks the ocelot switchdev driver such that it could
share the same implementation for LAG offload as the felix DSA driver.
Testing has been done in the following topology:
[...]