@ -2706,10 +2706,135 @@ static int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev,
return __mlxsw_sp_inetaddr_lag_event ( lag_dev , lag_dev , event , 1 ) ;
}
static struct mlxsw_sp_fid * mlxsw_sp_bridge_fid_get ( struct mlxsw_sp * mlxsw_sp ,
struct net_device * l3_dev )
{
u16 fid ;
if ( is_vlan_dev ( l3_dev ) )
fid = vlan_dev_vlan_id ( l3_dev ) ;
else if ( mlxsw_sp - > master_bridge . dev = = l3_dev )
fid = 1 ;
else
return mlxsw_sp_vfid_find ( mlxsw_sp , l3_dev ) ;
return mlxsw_sp_fid_find ( mlxsw_sp , fid ) ;
}
static enum mlxsw_reg_ritr_if_type mlxsw_sp_rif_type_get ( u16 fid )
{
if ( mlxsw_sp_fid_is_vfid ( fid ) )
return MLXSW_REG_RITR_FID_IF ;
else
return MLXSW_REG_RITR_VLAN_IF ;
}
static int mlxsw_sp_rif_bridge_op ( struct mlxsw_sp * mlxsw_sp ,
struct net_device * l3_dev ,
u16 fid , u16 rif ,
bool create )
{
enum mlxsw_reg_ritr_if_type rif_type ;
char ritr_pl [ MLXSW_REG_RITR_LEN ] ;
rif_type = mlxsw_sp_rif_type_get ( fid ) ;
mlxsw_reg_ritr_pack ( ritr_pl , create , rif_type , rif , l3_dev - > mtu ,
l3_dev - > dev_addr ) ;
mlxsw_reg_ritr_fid_set ( ritr_pl , rif_type , fid ) ;
return mlxsw_reg_write ( mlxsw_sp - > core , MLXSW_REG ( ritr ) , ritr_pl ) ;
}
static int mlxsw_sp_rif_bridge_create ( struct mlxsw_sp * mlxsw_sp ,
struct net_device * l3_dev ,
struct mlxsw_sp_fid * f )
{
struct mlxsw_sp_rif * r ;
u16 rif ;
int err ;
rif = mlxsw_sp_avail_rif_get ( mlxsw_sp ) ;
if ( rif = = MLXSW_SP_RIF_MAX )
return - ERANGE ;
err = mlxsw_sp_rif_bridge_op ( mlxsw_sp , l3_dev , f - > fid , rif , true ) ;
if ( err )
return err ;
err = mlxsw_sp_rif_fdb_op ( mlxsw_sp , l3_dev - > dev_addr , f - > fid , true ) ;
if ( err )
goto err_rif_fdb_op ;
r = mlxsw_sp_rif_alloc ( rif , l3_dev , f ) ;
if ( ! r ) {
err = - ENOMEM ;
goto err_rif_alloc ;
}
f - > r = r ;
mlxsw_sp - > rifs [ rif ] = r ;
netdev_dbg ( l3_dev , " RIF=%d created \n " , rif ) ;
return 0 ;
err_rif_alloc :
mlxsw_sp_rif_fdb_op ( mlxsw_sp , l3_dev - > dev_addr , f - > fid , false ) ;
err_rif_fdb_op :
mlxsw_sp_rif_bridge_op ( mlxsw_sp , l3_dev , f - > fid , rif , false ) ;
return err ;
}
void mlxsw_sp_rif_bridge_destroy ( struct mlxsw_sp * mlxsw_sp ,
struct mlxsw_sp_rif * r )
{
struct net_device * l3_dev = r - > dev ;
struct mlxsw_sp_fid * f = r - > f ;
u16 rif = r - > rif ;
mlxsw_sp - > rifs [ rif ] = NULL ;
f - > r = NULL ;
kfree ( r ) ;
mlxsw_sp_rif_fdb_op ( mlxsw_sp , l3_dev - > dev_addr , f - > fid , false ) ;
mlxsw_sp_rif_bridge_op ( mlxsw_sp , l3_dev , f - > fid , rif , false ) ;
netdev_dbg ( l3_dev , " RIF=%d destroyed \n " , rif ) ;
}
static int mlxsw_sp_inetaddr_bridge_event ( struct net_device * l3_dev ,
struct net_device * br_dev ,
unsigned long event )
{
struct mlxsw_sp * mlxsw_sp = mlxsw_sp_lower_get ( l3_dev ) ;
struct mlxsw_sp_fid * f ;
/* FID can either be an actual FID if the L3 device is the
* VLAN - aware bridge or a VLAN device on top . Otherwise , the
* L3 device is a VLAN - unaware bridge and we get a vFID .
*/
f = mlxsw_sp_bridge_fid_get ( mlxsw_sp , l3_dev ) ;
if ( WARN_ON ( ! f ) )
return - EINVAL ;
switch ( event ) {
case NETDEV_UP :
return mlxsw_sp_rif_bridge_create ( mlxsw_sp , l3_dev , f ) ;
case NETDEV_DOWN :
mlxsw_sp_rif_bridge_destroy ( mlxsw_sp , f - > r ) ;
break ;
}
return 0 ;
}
static int mlxsw_sp_inetaddr_vlan_event ( struct net_device * vlan_dev ,
unsigned long event )
{
struct net_device * real_dev = vlan_dev_real_dev ( vlan_dev ) ;
struct mlxsw_sp * mlxsw_sp = mlxsw_sp_lower_get ( vlan_dev ) ;
u16 vid = vlan_dev_vlan_id ( vlan_dev ) ;
if ( mlxsw_sp_port_dev_check ( real_dev ) )
@ -2718,6 +2843,10 @@ static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
else if ( netif_is_lag_master ( real_dev ) )
return __mlxsw_sp_inetaddr_lag_event ( vlan_dev , real_dev , event ,
vid ) ;
else if ( netif_is_bridge_master ( real_dev ) & &
mlxsw_sp - > master_bridge . dev = = real_dev )
return mlxsw_sp_inetaddr_bridge_event ( vlan_dev , real_dev ,
event ) ;
return 0 ;
}
@ -2743,6 +2872,8 @@ static int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
err = mlxsw_sp_inetaddr_port_event ( dev , event ) ;
else if ( netif_is_lag_master ( dev ) )
err = mlxsw_sp_inetaddr_lag_event ( dev , event ) ;
else if ( netif_is_bridge_master ( dev ) )
err = mlxsw_sp_inetaddr_bridge_event ( dev , dev , event ) ;
else if ( is_vlan_dev ( dev ) )
err = mlxsw_sp_inetaddr_vlan_event ( dev , event ) ;
@ -3416,6 +3547,8 @@ static void mlxsw_sp_master_bridge_vlan_unlink(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_fid * f ;
f = mlxsw_sp_fid_find ( mlxsw_sp , fid ) ;
if ( f & & f - > r )
mlxsw_sp_rif_bridge_destroy ( mlxsw_sp , f - > r ) ;
if ( f & & - - f - > ref_count = = 0 )
mlxsw_sp_fid_destroy ( mlxsw_sp , f ) ;
}
@ -3514,13 +3647,17 @@ static void mlxsw_sp_vfid_destroy(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_fid * f )
{
u16 vfid = mlxsw_sp_fid_to_vfid ( f - > fid ) ;
u16 fid = f - > fid ;
clear_bit ( vfid , mlxsw_sp - > vfids . mapped ) ;
list_del ( & f - > list ) ;
mlxsw_sp_vfid_op ( mlxsw_sp , f - > fid , false ) ;
if ( f - > r )
mlxsw_sp_rif_bridge_destroy ( mlxsw_sp , f - > r ) ;
kfree ( f ) ;
mlxsw_sp_vfid_op ( mlxsw_sp , fid , false ) ;
}
static int mlxsw_sp_vport_fid_map ( struct mlxsw_sp_port * mlxsw_sp_vport , u16 fid ,