Skip to content

Commit acc43b7

Browse files
vladimirolteanPaolo Abeni
authored andcommitted
net: dsa: allow masters to join a LAG
There are 2 ways in which a DSA user port may become handled by 2 CPU ports in a LAG: (1) its current DSA master joins a LAG ip link del bond0 && ip link add bond0 type bond mode 802.3ad ip link set eno2 master bond0 When this happens, all user ports with "eno2" as DSA master get automatically migrated to "bond0" as DSA master. (2) it is explicitly configured as such by the user # Before, the DSA master was eno3 ip link set swp0 type dsa master bond0 The design of this configuration is that the LAG device dynamically becomes a DSA master through dsa_master_setup() when the first physical DSA master becomes a LAG slave, and stops being so through dsa_master_teardown() when the last physical DSA master leaves. A LAG interface is considered as a valid DSA master only if it contains existing DSA masters, and no other lower interfaces. Therefore, we mainly rely on method (1) to enter this configuration. Each physical DSA master (LAG slave) retains its dev->dsa_ptr for when it becomes a standalone DSA master again. But the LAG master also has a dev->dsa_ptr, and this is actually duplicated from one of the physical LAG slaves, and therefore needs to be balanced when LAG slaves come and go. To the switch driver, putting DSA masters in a LAG is seen as putting their associated CPU ports in a LAG. We need to prepare cross-chip host FDB notifiers for CPU ports in a LAG, by calling the driver's ->lag_fdb_add method rather than ->port_fdb_add. Signed-off-by: Vladimir Oltean <[email protected]> Signed-off-by: Paolo Abeni <[email protected]>
1 parent 2e359b0 commit acc43b7

File tree

6 files changed

+310
-10
lines changed

6 files changed

+310
-10
lines changed

include/net/dsa.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,9 @@ struct dsa_port {
300300
u8 master_admin_up:1;
301301
u8 master_oper_up:1;
302302

303+
/* Valid only on user ports */
304+
u8 cpu_port_in_lag:1;
305+
303306
u8 setup:1;
304307

305308
struct device_node *dn;
@@ -724,6 +727,9 @@ static inline bool dsa_port_offloads_lag(struct dsa_port *dp,
724727

725728
static inline struct net_device *dsa_port_to_master(const struct dsa_port *dp)
726729
{
730+
if (dp->cpu_port_in_lag)
731+
return dsa_port_lag_dev_get(dp->cpu_dp);
732+
727733
return dp->cpu_dp->master;
728734
}
729735

@@ -811,6 +817,12 @@ dsa_tree_offloads_bridge_dev(struct dsa_switch_tree *dst,
811817
return false;
812818
}
813819

820+
static inline bool dsa_port_tree_same(const struct dsa_port *a,
821+
const struct dsa_port *b)
822+
{
823+
return a->ds->dst == b->ds->dst;
824+
}
825+
814826
typedef int dsa_fdb_dump_cb_t(const unsigned char *addr, u16 vid,
815827
bool is_static, void *data);
816828
struct dsa_switch_ops {

net/dsa/dsa_priv.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,11 @@ static inline int dsa_tag_protocol_overhead(const struct dsa_device_ops *ops)
185185
/* master.c */
186186
int dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp);
187187
void dsa_master_teardown(struct net_device *dev);
188+
int dsa_master_lag_setup(struct net_device *lag_dev, struct dsa_port *cpu_dp,
189+
struct netdev_lag_upper_info *uinfo,
190+
struct netlink_ext_ack *extack);
191+
void dsa_master_lag_teardown(struct net_device *lag_dev,
192+
struct dsa_port *cpu_dp);
188193

189194
static inline struct net_device *dsa_master_find_slave(struct net_device *dev,
190195
int device, int port)

net/dsa/master.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,3 +428,52 @@ void dsa_master_teardown(struct net_device *dev)
428428
*/
429429
wmb();
430430
}
431+
432+
int dsa_master_lag_setup(struct net_device *lag_dev, struct dsa_port *cpu_dp,
433+
struct netdev_lag_upper_info *uinfo,
434+
struct netlink_ext_ack *extack)
435+
{
436+
bool master_setup = false;
437+
int err;
438+
439+
if (!netdev_uses_dsa(lag_dev)) {
440+
err = dsa_master_setup(lag_dev, cpu_dp);
441+
if (err)
442+
return err;
443+
444+
master_setup = true;
445+
}
446+
447+
err = dsa_port_lag_join(cpu_dp, lag_dev, uinfo, extack);
448+
if (err) {
449+
if (extack && !extack->_msg)
450+
NL_SET_ERR_MSG_MOD(extack,
451+
"CPU port failed to join LAG");
452+
goto out_master_teardown;
453+
}
454+
455+
return 0;
456+
457+
out_master_teardown:
458+
if (master_setup)
459+
dsa_master_teardown(lag_dev);
460+
return err;
461+
}
462+
463+
/* Tear down a master if there isn't any other user port on it,
464+
* optionally also destroying LAG information.
465+
*/
466+
void dsa_master_lag_teardown(struct net_device *lag_dev,
467+
struct dsa_port *cpu_dp)
468+
{
469+
struct net_device *upper;
470+
struct list_head *iter;
471+
472+
dsa_port_lag_leave(cpu_dp, lag_dev);
473+
474+
netdev_for_each_upper_dev_rcu(lag_dev, upper, iter)
475+
if (dsa_slave_dev_check(upper))
476+
return;
477+
478+
dsa_master_teardown(lag_dev);
479+
}

net/dsa/port.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1393,6 +1393,7 @@ static int dsa_port_assign_master(struct dsa_port *dp,
13931393
return err;
13941394

13951395
dp->cpu_dp = master->dsa_ptr;
1396+
dp->cpu_port_in_lag = netif_is_lag_master(master);
13961397

13971398
return 0;
13981399
}

net/dsa/slave.c

Lines changed: 225 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2818,11 +2818,45 @@ dsa_slave_prechangeupper_sanity_check(struct net_device *dev,
28182818
return NOTIFY_DONE;
28192819
}
28202820

2821+
/* To be eligible as a DSA master, a LAG must have all lower interfaces be
2822+
* eligible DSA masters. Additionally, all LAG slaves must be DSA masters of
2823+
* switches in the same switch tree.
2824+
*/
2825+
static int dsa_lag_master_validate(struct net_device *lag_dev,
2826+
struct netlink_ext_ack *extack)
2827+
{
2828+
struct net_device *lower1, *lower2;
2829+
struct list_head *iter1, *iter2;
2830+
2831+
netdev_for_each_lower_dev(lag_dev, lower1, iter1) {
2832+
netdev_for_each_lower_dev(lag_dev, lower2, iter2) {
2833+
if (!netdev_uses_dsa(lower1) ||
2834+
!netdev_uses_dsa(lower2)) {
2835+
NL_SET_ERR_MSG_MOD(extack,
2836+
"All LAG ports must be eligible as DSA masters");
2837+
return notifier_from_errno(-EINVAL);
2838+
}
2839+
2840+
if (lower1 == lower2)
2841+
continue;
2842+
2843+
if (!dsa_port_tree_same(lower1->dsa_ptr,
2844+
lower2->dsa_ptr)) {
2845+
NL_SET_ERR_MSG_MOD(extack,
2846+
"LAG contains DSA masters of disjoint switch trees");
2847+
return notifier_from_errno(-EINVAL);
2848+
}
2849+
}
2850+
}
2851+
2852+
return NOTIFY_DONE;
2853+
}
2854+
28212855
static int
28222856
dsa_master_prechangeupper_sanity_check(struct net_device *master,
28232857
struct netdev_notifier_changeupper_info *info)
28242858
{
2825-
struct netlink_ext_ack *extack;
2859+
struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(&info->info);
28262860

28272861
if (!netdev_uses_dsa(master))
28282862
return NOTIFY_DONE;
@@ -2840,13 +2874,51 @@ dsa_master_prechangeupper_sanity_check(struct net_device *master,
28402874
if (netif_is_bridge_master(info->upper_dev))
28412875
return NOTIFY_DONE;
28422876

2843-
extack = netdev_notifier_info_to_extack(&info->info);
2877+
/* Allow LAG uppers, subject to further restrictions in
2878+
* dsa_lag_master_prechangelower_sanity_check()
2879+
*/
2880+
if (netif_is_lag_master(info->upper_dev))
2881+
return dsa_lag_master_validate(info->upper_dev, extack);
28442882

28452883
NL_SET_ERR_MSG_MOD(extack,
28462884
"DSA master cannot join unknown upper interfaces");
28472885
return notifier_from_errno(-EBUSY);
28482886
}
28492887

2888+
static int
2889+
dsa_lag_master_prechangelower_sanity_check(struct net_device *dev,
2890+
struct netdev_notifier_changeupper_info *info)
2891+
{
2892+
struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(&info->info);
2893+
struct net_device *lag_dev = info->upper_dev;
2894+
struct net_device *lower;
2895+
struct list_head *iter;
2896+
2897+
if (!netdev_uses_dsa(lag_dev) || !netif_is_lag_master(lag_dev))
2898+
return NOTIFY_DONE;
2899+
2900+
if (!info->linking)
2901+
return NOTIFY_DONE;
2902+
2903+
if (!netdev_uses_dsa(dev)) {
2904+
NL_SET_ERR_MSG(extack,
2905+
"Only DSA masters can join a LAG DSA master");
2906+
return notifier_from_errno(-EINVAL);
2907+
}
2908+
2909+
netdev_for_each_lower_dev(lag_dev, lower, iter) {
2910+
if (!dsa_port_tree_same(dev->dsa_ptr, lower->dsa_ptr)) {
2911+
NL_SET_ERR_MSG(extack,
2912+
"Interface is DSA master for a different switch tree than this LAG");
2913+
return notifier_from_errno(-EINVAL);
2914+
}
2915+
2916+
break;
2917+
}
2918+
2919+
return NOTIFY_DONE;
2920+
}
2921+
28502922
/* Don't allow bridging of DSA masters, since the bridge layer rx_handler
28512923
* prevents the DSA fake ethertype handler to be invoked, so we don't get the
28522924
* chance to strip off and parse the DSA switch tag protocol header (the bridge
@@ -2887,6 +2959,136 @@ dsa_bridge_prechangelower_sanity_check(struct net_device *new_lower,
28872959
return NOTIFY_DONE;
28882960
}
28892961

2962+
static void dsa_tree_migrate_ports_from_lag_master(struct dsa_switch_tree *dst,
2963+
struct net_device *lag_dev)
2964+
{
2965+
struct net_device *new_master = dsa_tree_find_first_master(dst);
2966+
struct dsa_port *dp;
2967+
int err;
2968+
2969+
dsa_tree_for_each_user_port(dp, dst) {
2970+
if (dsa_port_to_master(dp) != lag_dev)
2971+
continue;
2972+
2973+
err = dsa_slave_change_master(dp->slave, new_master, NULL);
2974+
if (err) {
2975+
netdev_err(dp->slave,
2976+
"failed to restore master to %s: %pe\n",
2977+
new_master->name, ERR_PTR(err));
2978+
}
2979+
}
2980+
}
2981+
2982+
static int dsa_master_lag_join(struct net_device *master,
2983+
struct net_device *lag_dev,
2984+
struct netdev_lag_upper_info *uinfo,
2985+
struct netlink_ext_ack *extack)
2986+
{
2987+
struct dsa_port *cpu_dp = master->dsa_ptr;
2988+
struct dsa_switch_tree *dst = cpu_dp->dst;
2989+
struct dsa_port *dp;
2990+
int err;
2991+
2992+
err = dsa_master_lag_setup(lag_dev, cpu_dp, uinfo, extack);
2993+
if (err)
2994+
return err;
2995+
2996+
dsa_tree_for_each_user_port(dp, dst) {
2997+
if (dsa_port_to_master(dp) != master)
2998+
continue;
2999+
3000+
err = dsa_slave_change_master(dp->slave, lag_dev, extack);
3001+
if (err)
3002+
goto restore;
3003+
}
3004+
3005+
return 0;
3006+
3007+
restore:
3008+
dsa_tree_for_each_user_port_continue_reverse(dp, dst) {
3009+
if (dsa_port_to_master(dp) != lag_dev)
3010+
continue;
3011+
3012+
err = dsa_slave_change_master(dp->slave, master, NULL);
3013+
if (err) {
3014+
netdev_err(dp->slave,
3015+
"failed to restore master to %s: %pe\n",
3016+
master->name, ERR_PTR(err));
3017+
}
3018+
}
3019+
3020+
dsa_master_lag_teardown(lag_dev, master->dsa_ptr);
3021+
3022+
return err;
3023+
}
3024+
3025+
static void dsa_master_lag_leave(struct net_device *master,
3026+
struct net_device *lag_dev)
3027+
{
3028+
struct dsa_port *dp, *cpu_dp = lag_dev->dsa_ptr;
3029+
struct dsa_switch_tree *dst = cpu_dp->dst;
3030+
struct dsa_port *new_cpu_dp = NULL;
3031+
struct net_device *lower;
3032+
struct list_head *iter;
3033+
3034+
netdev_for_each_lower_dev(lag_dev, lower, iter) {
3035+
if (netdev_uses_dsa(lower)) {
3036+
new_cpu_dp = lower->dsa_ptr;
3037+
break;
3038+
}
3039+
}
3040+
3041+
if (new_cpu_dp) {
3042+
/* Update the CPU port of the user ports still under the LAG
3043+
* so that dsa_port_to_master() continues to work properly
3044+
*/
3045+
dsa_tree_for_each_user_port(dp, dst)
3046+
if (dsa_port_to_master(dp) == lag_dev)
3047+
dp->cpu_dp = new_cpu_dp;
3048+
3049+
/* Update the index of the virtual CPU port to match the lowest
3050+
* physical CPU port
3051+
*/
3052+
lag_dev->dsa_ptr = new_cpu_dp;
3053+
wmb();
3054+
} else {
3055+
/* If the LAG DSA master has no ports left, migrate back all
3056+
* user ports to the first physical CPU port
3057+
*/
3058+
dsa_tree_migrate_ports_from_lag_master(dst, lag_dev);
3059+
}
3060+
3061+
/* This DSA master has left its LAG in any case, so let
3062+
* the CPU port leave the hardware LAG as well
3063+
*/
3064+
dsa_master_lag_teardown(lag_dev, master->dsa_ptr);
3065+
}
3066+
3067+
static int dsa_master_changeupper(struct net_device *dev,
3068+
struct netdev_notifier_changeupper_info *info)
3069+
{
3070+
struct netlink_ext_ack *extack;
3071+
int err = NOTIFY_DONE;
3072+
3073+
if (!netdev_uses_dsa(dev))
3074+
return err;
3075+
3076+
extack = netdev_notifier_info_to_extack(&info->info);
3077+
3078+
if (netif_is_lag_master(info->upper_dev)) {
3079+
if (info->linking) {
3080+
err = dsa_master_lag_join(dev, info->upper_dev,
3081+
info->upper_info, extack);
3082+
err = notifier_from_errno(err);
3083+
} else {
3084+
dsa_master_lag_leave(dev, info->upper_dev);
3085+
err = NOTIFY_OK;
3086+
}
3087+
}
3088+
3089+
return err;
3090+
}
3091+
28903092
static int dsa_slave_netdevice_event(struct notifier_block *nb,
28913093
unsigned long event, void *ptr)
28923094
{
@@ -2905,6 +3107,10 @@ static int dsa_slave_netdevice_event(struct notifier_block *nb,
29053107
if (notifier_to_errno(err))
29063108
return err;
29073109

3110+
err = dsa_lag_master_prechangelower_sanity_check(dev, info);
3111+
if (notifier_to_errno(err))
3112+
return err;
3113+
29083114
err = dsa_bridge_prechangelower_sanity_check(dev, info);
29093115
if (notifier_to_errno(err))
29103116
return err;
@@ -2930,19 +3136,32 @@ static int dsa_slave_netdevice_event(struct notifier_block *nb,
29303136
if (notifier_to_errno(err))
29313137
return err;
29323138

3139+
err = dsa_master_changeupper(dev, ptr);
3140+
if (notifier_to_errno(err))
3141+
return err;
3142+
29333143
break;
29343144
}
29353145
case NETDEV_CHANGELOWERSTATE: {
29363146
struct netdev_notifier_changelowerstate_info *info = ptr;
29373147
struct dsa_port *dp;
29383148
int err;
29393149

2940-
if (!dsa_slave_dev_check(dev))
2941-
break;
3150+
if (dsa_slave_dev_check(dev)) {
3151+
dp = dsa_slave_to_port(dev);
3152+
3153+
err = dsa_port_lag_change(dp, info->lower_state_info);
3154+
}
29423155

2943-
dp = dsa_slave_to_port(dev);
3156+
/* Mirror LAG port events on DSA masters that are in
3157+
* a LAG towards their respective switch CPU ports
3158+
*/
3159+
if (netdev_uses_dsa(dev)) {
3160+
dp = dev->dsa_ptr;
3161+
3162+
err = dsa_port_lag_change(dp, info->lower_state_info);
3163+
}
29443164

2945-
err = dsa_port_lag_change(dp, info->lower_state_info);
29463165
return notifier_from_errno(err);
29473166
}
29483167
case NETDEV_CHANGE:

0 commit comments

Comments
 (0)