aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorIdo Schimmel <idosch@mellanox.com>2016-07-04 02:23:16 -0400
committerDavid S. Miller <davem@davemloft.net>2016-07-04 21:25:16 -0400
commit701b186ebf52f872134824ffd8bd734c4c1e35df (patch)
treeab3672c3f1dd3dc65128f4fe850eafec8bdab061 /drivers
parent3ba2ebf4a2cbcf42fe471146d9d668865caa9f21 (diff)
mlxsw: spectrum: Configure FIDs based on bridge events
Before introducing support for L3 interfaces on top of the VLAN-aware bridge we need to add some missing infrastructure. Such an interface can either be the bridge device itself or a VLAN device on top of it. In the first case the router interface (RIF) is associated with FID 1, which is created whenever the first port netdev joins the bridge. We currently assume the default PVID is 1 and that it's already created, as it seems reasonable. This can be extended in the future. However, in the second case it's entirely possible we've yet to create a matching FID. This can happen if the VLAN device was configured before making any bridge port member in the VLAN. Prevent such ordering problems by using the VLAN device's CHANGEUPPER event to configure the FID. Make the VLAN device hold a reference to the FID and prevent it from being destroyed even if none of the port netdevs is using it. Signed-off-by: Ido Schimmel <idosch@mellanox.com> Signed-off-by: Jiri Pirko <jiri@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum.c86
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum.h27
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c18
3 files changed, 107 insertions, 24 deletions
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
index e987a8afab48..e49f80baeabe 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
@@ -2887,6 +2887,17 @@ int mlxsw_sp_port_fdb_flush(struct mlxsw_sp_port *mlxsw_sp_port, u16 fid)
2887 return mlxsw_sp_port_fdb_flush_by_port_fid(mlxsw_sp_port, fid); 2887 return mlxsw_sp_port_fdb_flush_by_port_fid(mlxsw_sp_port, fid);
2888} 2888}
2889 2889
2890static void mlxsw_sp_master_bridge_gone_sync(struct mlxsw_sp *mlxsw_sp)
2891{
2892 struct mlxsw_sp_fid *f, *tmp;
2893
2894 list_for_each_entry_safe(f, tmp, &mlxsw_sp->fids, list)
2895 if (--f->ref_count == 0)
2896 mlxsw_sp_fid_destroy(mlxsw_sp, f);
2897 else
2898 WARN_ON_ONCE(1);
2899}
2900
2890static bool mlxsw_sp_master_bridge_check(struct mlxsw_sp *mlxsw_sp, 2901static bool mlxsw_sp_master_bridge_check(struct mlxsw_sp *mlxsw_sp,
2891 struct net_device *br_dev) 2902 struct net_device *br_dev)
2892{ 2903{
@@ -2903,8 +2914,15 @@ static void mlxsw_sp_master_bridge_inc(struct mlxsw_sp *mlxsw_sp,
2903 2914
2904static void mlxsw_sp_master_bridge_dec(struct mlxsw_sp *mlxsw_sp) 2915static void mlxsw_sp_master_bridge_dec(struct mlxsw_sp *mlxsw_sp)
2905{ 2916{
2906 if (--mlxsw_sp->master_bridge.ref_count == 0) 2917 if (--mlxsw_sp->master_bridge.ref_count == 0) {
2907 mlxsw_sp->master_bridge.dev = NULL; 2918 mlxsw_sp->master_bridge.dev = NULL;
2919 /* It's possible upper VLAN devices are still holding
2920 * references to underlying FIDs. Drop the reference
2921 * and release the resources if it was the last one.
2922 * If it wasn't, then something bad happened.
2923 */
2924 mlxsw_sp_master_bridge_gone_sync(mlxsw_sp);
2925 }
2908} 2926}
2909 2927
2910static int mlxsw_sp_port_bridge_join(struct mlxsw_sp_port *mlxsw_sp_port, 2928static int mlxsw_sp_port_bridge_join(struct mlxsw_sp_port *mlxsw_sp_port,
@@ -3373,18 +3391,68 @@ static int mlxsw_sp_netdevice_lag_event(struct net_device *lag_dev,
3373 return 0; 3391 return 0;
3374} 3392}
3375 3393
3376static struct mlxsw_sp_fid * 3394static int mlxsw_sp_master_bridge_vlan_link(struct mlxsw_sp *mlxsw_sp,
3377mlxsw_sp_vfid_find(const struct mlxsw_sp *mlxsw_sp, 3395 struct net_device *vlan_dev)
3378 const struct net_device *br_dev)
3379{ 3396{
3397 u16 fid = vlan_dev_vlan_id(vlan_dev);
3380 struct mlxsw_sp_fid *f; 3398 struct mlxsw_sp_fid *f;
3381 3399
3382 list_for_each_entry(f, &mlxsw_sp->vfids.list, list) { 3400 f = mlxsw_sp_fid_find(mlxsw_sp, fid);
3383 if (f->dev == br_dev) 3401 if (!f) {
3384 return f; 3402 f = mlxsw_sp_fid_create(mlxsw_sp, fid);
3403 if (IS_ERR(f))
3404 return PTR_ERR(f);
3385 } 3405 }
3386 3406
3387 return NULL; 3407 f->ref_count++;
3408
3409 return 0;
3410}
3411
3412static void mlxsw_sp_master_bridge_vlan_unlink(struct mlxsw_sp *mlxsw_sp,
3413 struct net_device *vlan_dev)
3414{
3415 u16 fid = vlan_dev_vlan_id(vlan_dev);
3416 struct mlxsw_sp_fid *f;
3417
3418 f = mlxsw_sp_fid_find(mlxsw_sp, fid);
3419 if (f && --f->ref_count == 0)
3420 mlxsw_sp_fid_destroy(mlxsw_sp, f);
3421}
3422
3423static int mlxsw_sp_netdevice_bridge_event(struct net_device *br_dev,
3424 unsigned long event, void *ptr)
3425{
3426 struct netdev_notifier_changeupper_info *info;
3427 struct net_device *upper_dev;
3428 struct mlxsw_sp *mlxsw_sp;
3429 int err;
3430
3431 mlxsw_sp = mlxsw_sp_lower_get(br_dev);
3432 if (!mlxsw_sp)
3433 return 0;
3434 if (br_dev != mlxsw_sp->master_bridge.dev)
3435 return 0;
3436
3437 info = ptr;
3438
3439 switch (event) {
3440 case NETDEV_CHANGEUPPER:
3441 upper_dev = info->upper_dev;
3442 if (!is_vlan_dev(upper_dev))
3443 break;
3444 if (info->linking) {
3445 err = mlxsw_sp_master_bridge_vlan_link(mlxsw_sp,
3446 upper_dev);
3447 if (err)
3448 return err;
3449 } else {
3450 mlxsw_sp_master_bridge_vlan_unlink(mlxsw_sp, upper_dev);
3451 }
3452 break;
3453 }
3454
3455 return 0;
3388} 3456}
3389 3457
3390static u16 mlxsw_sp_avail_vfid_get(const struct mlxsw_sp *mlxsw_sp) 3458static u16 mlxsw_sp_avail_vfid_get(const struct mlxsw_sp *mlxsw_sp)
@@ -3675,6 +3743,8 @@ static int mlxsw_sp_netdevice_event(struct notifier_block *unused,
3675 err = mlxsw_sp_netdevice_port_event(dev, event, ptr); 3743 err = mlxsw_sp_netdevice_port_event(dev, event, ptr);
3676 else if (netif_is_lag_master(dev)) 3744 else if (netif_is_lag_master(dev))
3677 err = mlxsw_sp_netdevice_lag_event(dev, event, ptr); 3745 err = mlxsw_sp_netdevice_lag_event(dev, event, ptr);
3746 else if (netif_is_bridge_master(dev))
3747 err = mlxsw_sp_netdevice_bridge_event(dev, event, ptr);
3678 else if (is_vlan_dev(dev)) 3748 else if (is_vlan_dev(dev))
3679 err = mlxsw_sp_netdevice_vlan_event(dev, event, ptr); 3749 err = mlxsw_sp_netdevice_vlan_event(dev, event, ptr);
3680 3750
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
index b15b47b9161f..17c5d3b68f26 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
@@ -387,6 +387,31 @@ mlxsw_sp_port_vport_find_by_fid(const struct mlxsw_sp_port *mlxsw_sp_port,
387 return NULL; 387 return NULL;
388} 388}
389 389
390static inline struct mlxsw_sp_fid *mlxsw_sp_fid_find(struct mlxsw_sp *mlxsw_sp,
391 u16 fid)
392{
393 struct mlxsw_sp_fid *f;
394
395 list_for_each_entry(f, &mlxsw_sp->fids, list)
396 if (f->fid == fid)
397 return f;
398
399 return NULL;
400}
401
402static inline struct mlxsw_sp_fid *
403mlxsw_sp_vfid_find(const struct mlxsw_sp *mlxsw_sp,
404 const struct net_device *br_dev)
405{
406 struct mlxsw_sp_fid *f;
407
408 list_for_each_entry(f, &mlxsw_sp->vfids.list, list)
409 if (f->dev == br_dev)
410 return f;
411
412 return NULL;
413}
414
390static inline struct mlxsw_sp_rif * 415static inline struct mlxsw_sp_rif *
391mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp, 416mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
392 const struct net_device *dev) 417 const struct net_device *dev)
@@ -459,6 +484,8 @@ int mlxsw_sp_port_pvid_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid);
459int mlxsw_sp_port_fdb_flush(struct mlxsw_sp_port *mlxsw_sp_port, u16 fid); 484int mlxsw_sp_port_fdb_flush(struct mlxsw_sp_port *mlxsw_sp_port, u16 fid);
460int mlxsw_sp_rif_fdb_op(struct mlxsw_sp *mlxsw_sp, const char *mac, u16 fid, 485int mlxsw_sp_rif_fdb_op(struct mlxsw_sp *mlxsw_sp, const char *mac, u16 fid,
461 bool adding); 486 bool adding);
487struct mlxsw_sp_fid *mlxsw_sp_fid_create(struct mlxsw_sp *mlxsw_sp, u16 fid);
488void mlxsw_sp_fid_destroy(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *f);
462int mlxsw_sp_port_ets_set(struct mlxsw_sp_port *mlxsw_sp_port, 489int mlxsw_sp_port_ets_set(struct mlxsw_sp_port *mlxsw_sp_port,
463 enum mlxsw_reg_qeec_hr hr, u8 index, u8 next_index, 490 enum mlxsw_reg_qeec_hr hr, u8 index, u8 next_index,
464 bool dwrr, u8 dwrr_weight); 491 bool dwrr, u8 dwrr_weight);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
index 941acd786937..e446640e659b 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
@@ -374,18 +374,6 @@ static int mlxsw_sp_port_attr_set(struct net_device *dev,
374 return err; 374 return err;
375} 375}
376 376
377static struct mlxsw_sp_fid *mlxsw_sp_fid_find(struct mlxsw_sp *mlxsw_sp,
378 u16 fid)
379{
380 struct mlxsw_sp_fid *f;
381
382 list_for_each_entry(f, &mlxsw_sp->fids, list)
383 if (f->fid == fid)
384 return f;
385
386 return NULL;
387}
388
389static int mlxsw_sp_fid_op(struct mlxsw_sp *mlxsw_sp, u16 fid, bool create) 377static int mlxsw_sp_fid_op(struct mlxsw_sp *mlxsw_sp, u16 fid, bool create)
390{ 378{
391 char sfmr_pl[MLXSW_REG_SFMR_LEN]; 379 char sfmr_pl[MLXSW_REG_SFMR_LEN];
@@ -416,8 +404,7 @@ static struct mlxsw_sp_fid *mlxsw_sp_fid_alloc(u16 fid)
416 return f; 404 return f;
417} 405}
418 406
419static struct mlxsw_sp_fid *mlxsw_sp_fid_create(struct mlxsw_sp *mlxsw_sp, 407struct mlxsw_sp_fid *mlxsw_sp_fid_create(struct mlxsw_sp *mlxsw_sp, u16 fid)
420 u16 fid)
421{ 408{
422 struct mlxsw_sp_fid *f; 409 struct mlxsw_sp_fid *f;
423 int err; 410 int err;
@@ -452,8 +439,7 @@ err_fid_map:
452 return ERR_PTR(err); 439 return ERR_PTR(err);
453} 440}
454 441
455static void mlxsw_sp_fid_destroy(struct mlxsw_sp *mlxsw_sp, 442void mlxsw_sp_fid_destroy(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *f)
456 struct mlxsw_sp_fid *f)
457{ 443{
458 u16 fid = f->fid; 444 u16 fid = f->fid;
459 445