forked from mirrors/linux
		
	mlxsw: spectrum: Implement LAG port join/leave
Implement basic procedures for joining/leaving port to/from LAG. That includes HW setup of collector, core LAG mapping setup. Signed-off-by: Jiri Pirko <jiri@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									3b71571c01
								
							
						
					
					
						commit
						0d65fc1304
					
				
					 2 changed files with 306 additions and 17 deletions
				
			
		| 
						 | 
					@ -1712,6 +1712,22 @@ static int mlxsw_sp_flood_init(struct mlxsw_sp *mlxsw_sp)
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mlxsw_sp_lag_init(struct mlxsw_sp *mlxsw_sp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char slcr_pl[MLXSW_REG_SLCR_LEN];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mlxsw_reg_slcr_pack(slcr_pl, MLXSW_REG_SLCR_LAG_HASH_SMAC |
 | 
				
			||||||
 | 
									     MLXSW_REG_SLCR_LAG_HASH_DMAC |
 | 
				
			||||||
 | 
									     MLXSW_REG_SLCR_LAG_HASH_ETHERTYPE |
 | 
				
			||||||
 | 
									     MLXSW_REG_SLCR_LAG_HASH_VLANID |
 | 
				
			||||||
 | 
									     MLXSW_REG_SLCR_LAG_HASH_SIP |
 | 
				
			||||||
 | 
									     MLXSW_REG_SLCR_LAG_HASH_DIP |
 | 
				
			||||||
 | 
									     MLXSW_REG_SLCR_LAG_HASH_SPORT |
 | 
				
			||||||
 | 
									     MLXSW_REG_SLCR_LAG_HASH_DPORT |
 | 
				
			||||||
 | 
									     MLXSW_REG_SLCR_LAG_HASH_IPPROTO);
 | 
				
			||||||
 | 
						return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(slcr), slcr_pl);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int mlxsw_sp_init(void *priv, struct mlxsw_core *mlxsw_core,
 | 
					static int mlxsw_sp_init(void *priv, struct mlxsw_core *mlxsw_core,
 | 
				
			||||||
			 const struct mlxsw_bus_info *mlxsw_bus_info)
 | 
								 const struct mlxsw_bus_info *mlxsw_bus_info)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -1757,6 +1773,12 @@ static int mlxsw_sp_init(void *priv, struct mlxsw_core *mlxsw_core,
 | 
				
			||||||
		goto err_buffers_init;
 | 
							goto err_buffers_init;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = mlxsw_sp_lag_init(mlxsw_sp);
 | 
				
			||||||
 | 
						if (err) {
 | 
				
			||||||
 | 
							dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize LAG\n");
 | 
				
			||||||
 | 
							goto err_lag_init;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = mlxsw_sp_switchdev_init(mlxsw_sp);
 | 
						err = mlxsw_sp_switchdev_init(mlxsw_sp);
 | 
				
			||||||
	if (err) {
 | 
						if (err) {
 | 
				
			||||||
		dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize switchdev\n");
 | 
							dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize switchdev\n");
 | 
				
			||||||
| 
						 | 
					@ -1766,6 +1788,7 @@ static int mlxsw_sp_init(void *priv, struct mlxsw_core *mlxsw_core,
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
err_switchdev_init:
 | 
					err_switchdev_init:
 | 
				
			||||||
 | 
					err_lag_init:
 | 
				
			||||||
err_buffers_init:
 | 
					err_buffers_init:
 | 
				
			||||||
err_flood_init:
 | 
					err_flood_init:
 | 
				
			||||||
	mlxsw_sp_traps_fini(mlxsw_sp);
 | 
						mlxsw_sp_traps_fini(mlxsw_sp);
 | 
				
			||||||
| 
						 | 
					@ -1793,9 +1816,9 @@ static struct mlxsw_config_profile mlxsw_sp_config_profile = {
 | 
				
			||||||
	.used_max_vepa_channels		= 1,
 | 
						.used_max_vepa_channels		= 1,
 | 
				
			||||||
	.max_vepa_channels		= 0,
 | 
						.max_vepa_channels		= 0,
 | 
				
			||||||
	.used_max_lag			= 1,
 | 
						.used_max_lag			= 1,
 | 
				
			||||||
	.max_lag			= 64,
 | 
						.max_lag			= MLXSW_SP_LAG_MAX,
 | 
				
			||||||
	.used_max_port_per_lag		= 1,
 | 
						.used_max_port_per_lag		= 1,
 | 
				
			||||||
	.max_port_per_lag		= 16,
 | 
						.max_port_per_lag		= MLXSW_SP_PORT_PER_LAG_MAX,
 | 
				
			||||||
	.used_max_mid			= 1,
 | 
						.used_max_mid			= 1,
 | 
				
			||||||
	.max_mid			= 7000,
 | 
						.max_mid			= 7000,
 | 
				
			||||||
	.used_max_pgt			= 1,
 | 
						.used_max_pgt			= 1,
 | 
				
			||||||
| 
						 | 
					@ -1894,19 +1917,206 @@ static void mlxsw_sp_master_bridge_dec(struct mlxsw_sp *mlxsw_sp,
 | 
				
			||||||
		mlxsw_sp->master_bridge.dev = NULL;
 | 
							mlxsw_sp->master_bridge.dev = NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int mlxsw_sp_netdevice_event(struct notifier_block *unused,
 | 
					static int mlxsw_sp_lag_create(struct mlxsw_sp *mlxsw_sp, u16 lag_id)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char sldr_pl[MLXSW_REG_SLDR_LEN];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mlxsw_reg_sldr_lag_create_pack(sldr_pl, lag_id);
 | 
				
			||||||
 | 
						return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sldr), sldr_pl);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mlxsw_sp_lag_destroy(struct mlxsw_sp *mlxsw_sp, u16 lag_id)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char sldr_pl[MLXSW_REG_SLDR_LEN];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mlxsw_reg_sldr_lag_destroy_pack(sldr_pl, lag_id);
 | 
				
			||||||
 | 
						return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sldr), sldr_pl);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mlxsw_sp_lag_col_port_add(struct mlxsw_sp_port *mlxsw_sp_port,
 | 
				
			||||||
 | 
									     u16 lag_id, u8 port_index)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
 | 
				
			||||||
 | 
						char slcor_pl[MLXSW_REG_SLCOR_LEN];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mlxsw_reg_slcor_port_add_pack(slcor_pl, mlxsw_sp_port->local_port,
 | 
				
			||||||
 | 
									      lag_id, port_index);
 | 
				
			||||||
 | 
						return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(slcor), slcor_pl);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mlxsw_sp_lag_col_port_remove(struct mlxsw_sp_port *mlxsw_sp_port,
 | 
				
			||||||
 | 
										u16 lag_id)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
 | 
				
			||||||
 | 
						char slcor_pl[MLXSW_REG_SLCOR_LEN];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mlxsw_reg_slcor_port_remove_pack(slcor_pl, mlxsw_sp_port->local_port,
 | 
				
			||||||
 | 
										 lag_id);
 | 
				
			||||||
 | 
						return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(slcor), slcor_pl);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mlxsw_sp_lag_col_port_enable(struct mlxsw_sp_port *mlxsw_sp_port,
 | 
				
			||||||
 | 
										u16 lag_id)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
 | 
				
			||||||
 | 
						char slcor_pl[MLXSW_REG_SLCOR_LEN];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mlxsw_reg_slcor_col_enable_pack(slcor_pl, mlxsw_sp_port->local_port,
 | 
				
			||||||
 | 
										lag_id);
 | 
				
			||||||
 | 
						return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(slcor), slcor_pl);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mlxsw_sp_lag_col_port_disable(struct mlxsw_sp_port *mlxsw_sp_port,
 | 
				
			||||||
 | 
										 u16 lag_id)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
 | 
				
			||||||
 | 
						char slcor_pl[MLXSW_REG_SLCOR_LEN];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mlxsw_reg_slcor_col_disable_pack(slcor_pl, mlxsw_sp_port->local_port,
 | 
				
			||||||
 | 
										 lag_id);
 | 
				
			||||||
 | 
						return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(slcor), slcor_pl);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mlxsw_sp_lag_index_get(struct mlxsw_sp *mlxsw_sp,
 | 
				
			||||||
 | 
									  struct net_device *lag_dev,
 | 
				
			||||||
 | 
									  u16 *p_lag_id)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct mlxsw_sp_upper *lag;
 | 
				
			||||||
 | 
						int free_lag_id = -1;
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < MLXSW_SP_LAG_MAX; i++) {
 | 
				
			||||||
 | 
							lag = mlxsw_sp_lag_get(mlxsw_sp, i);
 | 
				
			||||||
 | 
							if (lag->ref_count) {
 | 
				
			||||||
 | 
								if (lag->dev == lag_dev) {
 | 
				
			||||||
 | 
									*p_lag_id = i;
 | 
				
			||||||
 | 
									return 0;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else if (free_lag_id < 0) {
 | 
				
			||||||
 | 
								free_lag_id = i;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (free_lag_id < 0)
 | 
				
			||||||
 | 
							return -EBUSY;
 | 
				
			||||||
 | 
						*p_lag_id = free_lag_id;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool
 | 
				
			||||||
 | 
					mlxsw_sp_master_lag_check(struct mlxsw_sp *mlxsw_sp,
 | 
				
			||||||
 | 
								  struct net_device *lag_dev,
 | 
				
			||||||
 | 
								  struct netdev_lag_upper_info *lag_upper_info)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u16 lag_id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (mlxsw_sp_lag_index_get(mlxsw_sp, lag_dev, &lag_id) != 0)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						if (lag_upper_info->tx_type != NETDEV_LAG_TX_TYPE_HASH)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mlxsw_sp_port_lag_index_get(struct mlxsw_sp *mlxsw_sp,
 | 
				
			||||||
 | 
									       u16 lag_id, u8 *p_port_index)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < MLXSW_SP_PORT_PER_LAG_MAX; i++) {
 | 
				
			||||||
 | 
							if (!mlxsw_sp_port_lagged_get(mlxsw_sp, lag_id, i)) {
 | 
				
			||||||
 | 
								*p_port_index = i;
 | 
				
			||||||
 | 
								return 0;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return -EBUSY;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mlxsw_sp_port_lag_join(struct mlxsw_sp_port *mlxsw_sp_port,
 | 
				
			||||||
 | 
									  struct net_device *lag_dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
 | 
				
			||||||
 | 
						struct mlxsw_sp_upper *lag;
 | 
				
			||||||
 | 
						u16 lag_id;
 | 
				
			||||||
 | 
						u8 port_index;
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = mlxsw_sp_lag_index_get(mlxsw_sp, lag_dev, &lag_id);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
						lag = mlxsw_sp_lag_get(mlxsw_sp, lag_id);
 | 
				
			||||||
 | 
						if (!lag->ref_count) {
 | 
				
			||||||
 | 
							err = mlxsw_sp_lag_create(mlxsw_sp, lag_id);
 | 
				
			||||||
 | 
							if (err)
 | 
				
			||||||
 | 
								return err;
 | 
				
			||||||
 | 
							lag->dev = lag_dev;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = mlxsw_sp_port_lag_index_get(mlxsw_sp, lag_id, &port_index);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
						err = mlxsw_sp_lag_col_port_add(mlxsw_sp_port, lag_id, port_index);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							goto err_col_port_add;
 | 
				
			||||||
 | 
						err = mlxsw_sp_lag_col_port_enable(mlxsw_sp_port, lag_id);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							goto err_col_port_enable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mlxsw_core_lag_mapping_set(mlxsw_sp->core, lag_id, port_index,
 | 
				
			||||||
 | 
									   mlxsw_sp_port->local_port);
 | 
				
			||||||
 | 
						mlxsw_sp_port->lag_id = lag_id;
 | 
				
			||||||
 | 
						mlxsw_sp_port->lagged = 1;
 | 
				
			||||||
 | 
						lag->ref_count++;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					err_col_port_add:
 | 
				
			||||||
 | 
						if (!lag->ref_count)
 | 
				
			||||||
 | 
							mlxsw_sp_lag_destroy(mlxsw_sp, lag_id);
 | 
				
			||||||
 | 
					err_col_port_enable:
 | 
				
			||||||
 | 
						mlxsw_sp_lag_col_port_remove(mlxsw_sp_port, lag_id);
 | 
				
			||||||
 | 
						return err;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mlxsw_sp_port_lag_leave(struct mlxsw_sp_port *mlxsw_sp_port,
 | 
				
			||||||
 | 
									   struct net_device *lag_dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
 | 
				
			||||||
 | 
						struct mlxsw_sp_upper *lag;
 | 
				
			||||||
 | 
						u16 lag_id = mlxsw_sp_port->lag_id;
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!mlxsw_sp_port->lagged)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						lag = mlxsw_sp_lag_get(mlxsw_sp, lag_id);
 | 
				
			||||||
 | 
						WARN_ON(lag->ref_count == 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = mlxsw_sp_lag_col_port_disable(mlxsw_sp_port, lag_id);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
						mlxsw_sp_lag_col_port_remove(mlxsw_sp_port, lag_id);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (lag->ref_count == 1) {
 | 
				
			||||||
 | 
							err = mlxsw_sp_lag_destroy(mlxsw_sp, lag_id);
 | 
				
			||||||
 | 
							if (err)
 | 
				
			||||||
 | 
								return err;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mlxsw_core_lag_mapping_clear(mlxsw_sp->core, lag_id,
 | 
				
			||||||
 | 
									     mlxsw_sp_port->local_port);
 | 
				
			||||||
 | 
						mlxsw_sp_port->lagged = 0;
 | 
				
			||||||
 | 
						lag->ref_count--;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mlxsw_sp_netdevice_port_event(struct net_device *dev,
 | 
				
			||||||
					 unsigned long event, void *ptr)
 | 
										 unsigned long event, void *ptr)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 | 
					 | 
				
			||||||
	struct netdev_notifier_changeupper_info *info;
 | 
						struct netdev_notifier_changeupper_info *info;
 | 
				
			||||||
	struct mlxsw_sp_port *mlxsw_sp_port;
 | 
						struct mlxsw_sp_port *mlxsw_sp_port;
 | 
				
			||||||
	struct net_device *upper_dev;
 | 
						struct net_device *upper_dev;
 | 
				
			||||||
	struct mlxsw_sp *mlxsw_sp;
 | 
						struct mlxsw_sp *mlxsw_sp;
 | 
				
			||||||
	int err;
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!mlxsw_sp_port_dev_check(dev))
 | 
					 | 
				
			||||||
		return NOTIFY_DONE;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	mlxsw_sp_port = netdev_priv(dev);
 | 
						mlxsw_sp_port = netdev_priv(dev);
 | 
				
			||||||
	mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
 | 
						mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
 | 
				
			||||||
	info = ptr;
 | 
						info = ptr;
 | 
				
			||||||
| 
						 | 
					@ -1914,16 +2124,22 @@ static int mlxsw_sp_netdevice_event(struct notifier_block *unused,
 | 
				
			||||||
	switch (event) {
 | 
						switch (event) {
 | 
				
			||||||
	case NETDEV_PRECHANGEUPPER:
 | 
						case NETDEV_PRECHANGEUPPER:
 | 
				
			||||||
		upper_dev = info->upper_dev;
 | 
							upper_dev = info->upper_dev;
 | 
				
			||||||
 | 
							if (!info->master || !info->linking)
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
		/* HW limitation forbids to put ports to multiple bridges. */
 | 
							/* HW limitation forbids to put ports to multiple bridges. */
 | 
				
			||||||
		if (info->master && info->linking &&
 | 
							if (netif_is_bridge_master(upper_dev) &&
 | 
				
			||||||
		    netif_is_bridge_master(upper_dev) &&
 | 
					 | 
				
			||||||
		    !mlxsw_sp_master_bridge_check(mlxsw_sp, upper_dev))
 | 
							    !mlxsw_sp_master_bridge_check(mlxsw_sp, upper_dev))
 | 
				
			||||||
			return NOTIFY_BAD;
 | 
								return NOTIFY_BAD;
 | 
				
			||||||
 | 
							if (netif_is_lag_master(upper_dev) &&
 | 
				
			||||||
 | 
							    !mlxsw_sp_master_lag_check(mlxsw_sp, upper_dev,
 | 
				
			||||||
 | 
										       info->upper_info))
 | 
				
			||||||
 | 
								return NOTIFY_BAD;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case NETDEV_CHANGEUPPER:
 | 
						case NETDEV_CHANGEUPPER:
 | 
				
			||||||
		upper_dev = info->upper_dev;
 | 
							upper_dev = info->upper_dev;
 | 
				
			||||||
		if (info->master &&
 | 
							if (!info->master)
 | 
				
			||||||
		    netif_is_bridge_master(upper_dev)) {
 | 
								break;
 | 
				
			||||||
 | 
							if (netif_is_bridge_master(upper_dev)) {
 | 
				
			||||||
			if (info->linking) {
 | 
								if (info->linking) {
 | 
				
			||||||
				err = mlxsw_sp_port_bridge_join(mlxsw_sp_port);
 | 
									err = mlxsw_sp_port_bridge_join(mlxsw_sp_port);
 | 
				
			||||||
				if (err)
 | 
									if (err)
 | 
				
			||||||
| 
						 | 
					@ -1937,6 +2153,22 @@ static int mlxsw_sp_netdevice_event(struct notifier_block *unused,
 | 
				
			||||||
				mlxsw_sp_port->bridged = 0;
 | 
									mlxsw_sp_port->bridged = 0;
 | 
				
			||||||
				mlxsw_sp_master_bridge_dec(mlxsw_sp, upper_dev);
 | 
									mlxsw_sp_master_bridge_dec(mlxsw_sp, upper_dev);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
							} else if (netif_is_lag_master(upper_dev)) {
 | 
				
			||||||
 | 
								if (info->linking) {
 | 
				
			||||||
 | 
									err = mlxsw_sp_port_lag_join(mlxsw_sp_port,
 | 
				
			||||||
 | 
												     upper_dev);
 | 
				
			||||||
 | 
									if (err) {
 | 
				
			||||||
 | 
										netdev_err(dev, "Failed to join link aggregation\n");
 | 
				
			||||||
 | 
										return NOTIFY_BAD;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									err = mlxsw_sp_port_lag_leave(mlxsw_sp_port,
 | 
				
			||||||
 | 
												      upper_dev);
 | 
				
			||||||
 | 
									if (err) {
 | 
				
			||||||
 | 
										netdev_err(dev, "Failed to leave link aggregation\n");
 | 
				
			||||||
 | 
										return NOTIFY_BAD;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -1944,6 +2176,38 @@ static int mlxsw_sp_netdevice_event(struct notifier_block *unused,
 | 
				
			||||||
	return NOTIFY_DONE;
 | 
						return NOTIFY_DONE;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mlxsw_sp_netdevice_lag_event(struct net_device *lag_dev,
 | 
				
			||||||
 | 
										unsigned long event, void *ptr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct net_device *dev;
 | 
				
			||||||
 | 
						struct list_head *iter;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						netdev_for_each_lower_dev(lag_dev, dev, iter) {
 | 
				
			||||||
 | 
							if (mlxsw_sp_port_dev_check(dev)) {
 | 
				
			||||||
 | 
								ret = mlxsw_sp_netdevice_port_event(dev, event, ptr);
 | 
				
			||||||
 | 
								if (ret == NOTIFY_BAD)
 | 
				
			||||||
 | 
									return ret;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return NOTIFY_DONE;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mlxsw_sp_netdevice_event(struct notifier_block *unused,
 | 
				
			||||||
 | 
									    unsigned long event, void *ptr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (mlxsw_sp_port_dev_check(dev))
 | 
				
			||||||
 | 
							return mlxsw_sp_netdevice_port_event(dev, event, ptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (netif_is_lag_master(dev))
 | 
				
			||||||
 | 
							return mlxsw_sp_netdevice_lag_event(dev, event, ptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return NOTIFY_DONE;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct notifier_block mlxsw_sp_netdevice_nb __read_mostly = {
 | 
					static struct notifier_block mlxsw_sp_netdevice_nb __read_mostly = {
 | 
				
			||||||
	.notifier_call = mlxsw_sp_netdevice_event,
 | 
						.notifier_call = mlxsw_sp_netdevice_event,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -46,9 +46,16 @@
 | 
				
			||||||
#include "core.h"
 | 
					#include "core.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define MLXSW_SP_VFID_BASE VLAN_N_VID
 | 
					#define MLXSW_SP_VFID_BASE VLAN_N_VID
 | 
				
			||||||
 | 
					#define MLXSW_SP_LAG_MAX 64
 | 
				
			||||||
 | 
					#define MLXSW_SP_PORT_PER_LAG_MAX 16
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct mlxsw_sp_port;
 | 
					struct mlxsw_sp_port;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct mlxsw_sp_upper {
 | 
				
			||||||
 | 
						struct net_device *dev;
 | 
				
			||||||
 | 
						unsigned int ref_count;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct mlxsw_sp {
 | 
					struct mlxsw_sp {
 | 
				
			||||||
	unsigned long active_vfids[BITS_TO_LONGS(VLAN_N_VID)];
 | 
						unsigned long active_vfids[BITS_TO_LONGS(VLAN_N_VID)];
 | 
				
			||||||
	unsigned long active_fids[BITS_TO_LONGS(VLAN_N_VID)];
 | 
						unsigned long active_fids[BITS_TO_LONGS(VLAN_N_VID)];
 | 
				
			||||||
| 
						 | 
					@ -63,12 +70,16 @@ struct mlxsw_sp {
 | 
				
			||||||
	} fdb_notify;
 | 
						} fdb_notify;
 | 
				
			||||||
#define MLXSW_SP_DEFAULT_AGEING_TIME 300
 | 
					#define MLXSW_SP_DEFAULT_AGEING_TIME 300
 | 
				
			||||||
	u32 ageing_time;
 | 
						u32 ageing_time;
 | 
				
			||||||
	struct {
 | 
						struct mlxsw_sp_upper master_bridge;
 | 
				
			||||||
		struct net_device *dev;
 | 
						struct mlxsw_sp_upper lags[MLXSW_SP_LAG_MAX];
 | 
				
			||||||
		unsigned int ref_count;
 | 
					 | 
				
			||||||
	} master_bridge;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline struct mlxsw_sp_upper *
 | 
				
			||||||
 | 
					mlxsw_sp_lag_get(struct mlxsw_sp *mlxsw_sp, u16 lag_id)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return &mlxsw_sp->lags[lag_id];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct mlxsw_sp_port_pcpu_stats {
 | 
					struct mlxsw_sp_port_pcpu_stats {
 | 
				
			||||||
	u64			rx_packets;
 | 
						u64			rx_packets;
 | 
				
			||||||
	u64			rx_bytes;
 | 
						u64			rx_bytes;
 | 
				
			||||||
| 
						 | 
					@ -87,8 +98,10 @@ struct mlxsw_sp_port {
 | 
				
			||||||
	u8 learning:1,
 | 
						u8 learning:1,
 | 
				
			||||||
	   learning_sync:1,
 | 
						   learning_sync:1,
 | 
				
			||||||
	   uc_flood:1,
 | 
						   uc_flood:1,
 | 
				
			||||||
	   bridged:1;
 | 
						   bridged:1,
 | 
				
			||||||
 | 
						   lagged:1;
 | 
				
			||||||
	u16 pvid;
 | 
						u16 pvid;
 | 
				
			||||||
 | 
						u16 lag_id;
 | 
				
			||||||
	/* 802.1Q bridge VLANs */
 | 
						/* 802.1Q bridge VLANs */
 | 
				
			||||||
	unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
 | 
						unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
 | 
				
			||||||
	/* VLAN interfaces */
 | 
						/* VLAN interfaces */
 | 
				
			||||||
| 
						 | 
					@ -96,6 +109,18 @@ struct mlxsw_sp_port {
 | 
				
			||||||
	u16 nr_vfids;
 | 
						u16 nr_vfids;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline struct mlxsw_sp_port *
 | 
				
			||||||
 | 
					mlxsw_sp_port_lagged_get(struct mlxsw_sp *mlxsw_sp, u16 lag_id, u8 port_index)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct mlxsw_sp_port *mlxsw_sp_port;
 | 
				
			||||||
 | 
						u8 local_port;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						local_port = mlxsw_core_lag_mapping_get(mlxsw_sp->core,
 | 
				
			||||||
 | 
											lag_id, port_index);
 | 
				
			||||||
 | 
						mlxsw_sp_port = mlxsw_sp->ports[local_port];
 | 
				
			||||||
 | 
						return mlxsw_sp_port && mlxsw_sp_port->lagged ? mlxsw_sp_port : NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum mlxsw_sp_flood_table {
 | 
					enum mlxsw_sp_flood_table {
 | 
				
			||||||
	MLXSW_SP_FLOOD_TABLE_UC,
 | 
						MLXSW_SP_FLOOD_TABLE_UC,
 | 
				
			||||||
	MLXSW_SP_FLOOD_TABLE_BM,
 | 
						MLXSW_SP_FLOOD_TABLE_BM,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue