diff options
Diffstat (limited to 'drivers/net/ethernet/mellanox/mlxsw')
24 files changed, 2477 insertions, 718 deletions
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index f080fab3de2b..3fd3812b8f31 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -90,7 +90,6 @@ struct mlxsw_core { struct devlink_health_reporter *fw_fatal; } health; struct mlxsw_env *env; - bool is_initialized; /* Denotes if core was already initialized. */ unsigned long driver_priv[]; /* driver_priv has to be always the last item */ }; @@ -1975,12 +1974,6 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, goto err_emad_init; if (!reload) { - err = devlink_register(devlink); - if (err) - goto err_devlink_register; - } - - if (!reload) { err = mlxsw_core_params_register(mlxsw_core); if (err) goto err_register_params; @@ -1995,12 +1988,6 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, if (err) goto err_health_init; - if (mlxsw_driver->init) { - err = mlxsw_driver->init(mlxsw_core, mlxsw_bus_info, extack); - if (err) - goto err_driver_init; - } - err = mlxsw_hwmon_init(mlxsw_core, mlxsw_bus_info, &mlxsw_core->hwmon); if (err) goto err_hwmon_init; @@ -2014,31 +2001,31 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, if (err) goto err_env_init; - mlxsw_core->is_initialized = true; - devlink_params_publish(devlink); - - if (!reload) - devlink_reload_enable(devlink); + if (mlxsw_driver->init) { + err = mlxsw_driver->init(mlxsw_core, mlxsw_bus_info, extack); + if (err) + goto err_driver_init; + } + if (!reload) { + devlink_set_features(devlink, DEVLINK_F_RELOAD); + devlink_register(devlink); + } return 0; +err_driver_init: + mlxsw_env_fini(mlxsw_core->env); err_env_init: mlxsw_thermal_fini(mlxsw_core->thermal); err_thermal_init: mlxsw_hwmon_fini(mlxsw_core->hwmon); err_hwmon_init: - if (mlxsw_core->driver->fini) - mlxsw_core->driver->fini(mlxsw_core); -err_driver_init: mlxsw_core_health_fini(mlxsw_core); err_health_init: err_fw_rev_validate: if (!reload) mlxsw_core_params_unregister(mlxsw_core); err_register_params: - if (!reload) - devlink_unregister(devlink); -err_devlink_register: mlxsw_emad_fini(mlxsw_core); err_emad_init: kfree(mlxsw_core->lag.mapping); @@ -2088,7 +2075,8 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core, struct devlink *devlink = priv_to_devlink(mlxsw_core); if (!reload) - devlink_reload_disable(devlink); + devlink_unregister(devlink); + if (devlink_is_reload_failed(devlink)) { if (!reload) /* Only the parts that were not de-initialized in the @@ -2099,18 +2087,14 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core, return; } - devlink_params_unpublish(devlink); - mlxsw_core->is_initialized = false; + if (mlxsw_core->driver->fini) + mlxsw_core->driver->fini(mlxsw_core); mlxsw_env_fini(mlxsw_core->env); mlxsw_thermal_fini(mlxsw_core->thermal); mlxsw_hwmon_fini(mlxsw_core->hwmon); - if (mlxsw_core->driver->fini) - mlxsw_core->driver->fini(mlxsw_core); mlxsw_core_health_fini(mlxsw_core); if (!reload) mlxsw_core_params_unregister(mlxsw_core); - if (!reload) - devlink_unregister(devlink); mlxsw_emad_fini(mlxsw_core); kfree(mlxsw_core->lag.mapping); mlxsw_ports_fini(mlxsw_core, reload); @@ -2124,7 +2108,6 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core, reload_fail_deinit: mlxsw_core_params_unregister(mlxsw_core); - devlink_unregister(devlink); devlink_resources_unregister(devlink, NULL); devlink_free(devlink); } @@ -2939,49 +2922,6 @@ struct mlxsw_env *mlxsw_core_env(const struct mlxsw_core *mlxsw_core) return mlxsw_core->env; } -bool mlxsw_core_is_initialized(const struct mlxsw_core *mlxsw_core) -{ - return mlxsw_core->is_initialized; -} - -int mlxsw_core_module_max_width(struct mlxsw_core *mlxsw_core, u8 module) -{ - enum mlxsw_reg_pmtm_module_type module_type; - char pmtm_pl[MLXSW_REG_PMTM_LEN]; - int err; - - mlxsw_reg_pmtm_pack(pmtm_pl, module); - err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(pmtm), pmtm_pl); - if (err) - return err; - mlxsw_reg_pmtm_unpack(pmtm_pl, &module_type); - - /* Here we need to get the module width according to the module type. */ - - switch (module_type) { - case MLXSW_REG_PMTM_MODULE_TYPE_C2C8X: - case MLXSW_REG_PMTM_MODULE_TYPE_QSFP_DD: - case MLXSW_REG_PMTM_MODULE_TYPE_OSFP: - return 8; - case MLXSW_REG_PMTM_MODULE_TYPE_C2C4X: - case MLXSW_REG_PMTM_MODULE_TYPE_BP_4X: - case MLXSW_REG_PMTM_MODULE_TYPE_QSFP: - return 4; - case MLXSW_REG_PMTM_MODULE_TYPE_C2C2X: - case MLXSW_REG_PMTM_MODULE_TYPE_BP_2X: - case MLXSW_REG_PMTM_MODULE_TYPE_SFP_DD: - case MLXSW_REG_PMTM_MODULE_TYPE_DSFP: - return 2; - case MLXSW_REG_PMTM_MODULE_TYPE_C2C1X: - case MLXSW_REG_PMTM_MODULE_TYPE_BP_1X: - case MLXSW_REG_PMTM_MODULE_TYPE_SFP: - return 1; - default: - return -EINVAL; - } -} -EXPORT_SYMBOL(mlxsw_core_module_max_width); - static void mlxsw_core_buf_dump_dbg(struct mlxsw_core *mlxsw_core, const char *buf, size_t size) { diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index 80712dc803d0..12023a550007 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h @@ -249,8 +249,6 @@ mlxsw_core_port_devlink_port_get(struct mlxsw_core *mlxsw_core, u8 local_port); bool mlxsw_core_port_is_xm(const struct mlxsw_core *mlxsw_core, u8 local_port); struct mlxsw_env *mlxsw_core_env(const struct mlxsw_core *mlxsw_core); -bool mlxsw_core_is_initialized(const struct mlxsw_core *mlxsw_core); -int mlxsw_core_module_max_width(struct mlxsw_core *mlxsw_core, u8 module); int mlxsw_core_schedule_dw(struct delayed_work *dwork, unsigned long delay); bool mlxsw_core_schedule_work(struct work_struct *work); diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_env.c b/drivers/net/ethernet/mellanox/mlxsw/core_env.c index 3713c45cfa1e..6dd4ae2f45f4 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_env.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_env.c @@ -5,6 +5,7 @@ #include <linux/err.h> #include <linux/ethtool.h> #include <linux/sfp.h> +#include <linux/mutex.h> #include "core.h" #include "core_env.h" @@ -14,12 +15,15 @@ struct mlxsw_env_module_info { u64 module_overheat_counter; bool is_overheat; + int num_ports_mapped; + int num_ports_up; + enum ethtool_module_power_mode_policy power_mode_policy; }; struct mlxsw_env { struct mlxsw_core *core; u8 module_count; - spinlock_t module_info_lock; /* Protects 'module_info'. */ + struct mutex module_info_lock; /* Protects 'module_info'. */ struct mlxsw_env_module_info module_info[]; }; @@ -389,6 +393,205 @@ mlxsw_env_get_module_eeprom_by_page(struct mlxsw_core *mlxsw_core, u8 module, } EXPORT_SYMBOL(mlxsw_env_get_module_eeprom_by_page); +static int mlxsw_env_module_reset(struct mlxsw_core *mlxsw_core, u8 module) +{ + char pmaos_pl[MLXSW_REG_PMAOS_LEN]; + + mlxsw_reg_pmaos_pack(pmaos_pl, module); + mlxsw_reg_pmaos_rst_set(pmaos_pl, true); + + return mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), pmaos_pl); +} + +int mlxsw_env_reset_module(struct net_device *netdev, + struct mlxsw_core *mlxsw_core, u8 module, u32 *flags) +{ + struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core); + u32 req = *flags; + int err; + + if (!(req & ETH_RESET_PHY) && + !(req & (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT))) + return 0; + + if (WARN_ON_ONCE(module >= mlxsw_env->module_count)) + return -EINVAL; + + mutex_lock(&mlxsw_env->module_info_lock); + + if (mlxsw_env->module_info[module].num_ports_up) { + netdev_err(netdev, "Cannot reset module when ports using it are administratively up\n"); + err = -EINVAL; + goto out; + } + + if (mlxsw_env->module_info[module].num_ports_mapped > 1 && + !(req & (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT))) { + netdev_err(netdev, "Cannot reset module without \"phy-shared\" flag when shared by multiple ports\n"); + err = -EINVAL; + goto out; + } + + err = mlxsw_env_module_reset(mlxsw_core, module); + if (err) { + netdev_err(netdev, "Failed to reset module\n"); + goto out; + } + + *flags &= ~(ETH_RESET_PHY | (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT)); + +out: + mutex_unlock(&mlxsw_env->module_info_lock); + return err; +} +EXPORT_SYMBOL(mlxsw_env_reset_module); + +int +mlxsw_env_get_module_power_mode(struct mlxsw_core *mlxsw_core, u8 module, + struct ethtool_module_power_mode_params *params, + struct netlink_ext_ack *extack) +{ + struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core); + char mcion_pl[MLXSW_REG_MCION_LEN]; + u32 status_bits; + int err; + + if (WARN_ON_ONCE(module >= mlxsw_env->module_count)) + return -EINVAL; + + mutex_lock(&mlxsw_env->module_info_lock); + + params->policy = mlxsw_env->module_info[module].power_mode_policy; + + mlxsw_reg_mcion_pack(mcion_pl, module); + err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcion), mcion_pl); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Failed to retrieve module's power mode"); + goto out; + } + + status_bits = mlxsw_reg_mcion_module_status_bits_get(mcion_pl); + if (!(status_bits & MLXSW_REG_MCION_MODULE_STATUS_BITS_PRESENT_MASK)) + goto out; + + if (status_bits & MLXSW_REG_MCION_MODULE_STATUS_BITS_LOW_POWER_MASK) + params->mode = ETHTOOL_MODULE_POWER_MODE_LOW; + else + params->mode = ETHTOOL_MODULE_POWER_MODE_HIGH; + +out: + mutex_unlock(&mlxsw_env->module_info_lock); + return err; +} +EXPORT_SYMBOL(mlxsw_env_get_module_power_mode); + +static int mlxsw_env_module_enable_set(struct mlxsw_core *mlxsw_core, + u8 module, bool enable) +{ + enum mlxsw_reg_pmaos_admin_status admin_status; + char pmaos_pl[MLXSW_REG_PMAOS_LEN]; + + mlxsw_reg_pmaos_pack(pmaos_pl, module); + admin_status = enable ? MLXSW_REG_PMAOS_ADMIN_STATUS_ENABLED : + MLXSW_REG_PMAOS_ADMIN_STATUS_DISABLED; + mlxsw_reg_pmaos_admin_status_set(pmaos_pl, admin_status); + mlxsw_reg_pmaos_ase_set(pmaos_pl, true); + + return mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), pmaos_pl); +} + +static int mlxsw_env_module_low_power_set(struct mlxsw_core *mlxsw_core, + u8 module, bool low_power) +{ + u16 eeprom_override_mask, eeprom_override; + char pmmp_pl[MLXSW_REG_PMMP_LEN]; + + mlxsw_reg_pmmp_pack(pmmp_pl, module); + mlxsw_reg_pmmp_sticky_set(pmmp_pl, true); + /* Mask all the bits except low power mode. */ + eeprom_override_mask = ~MLXSW_REG_PMMP_EEPROM_OVERRIDE_LOW_POWER_MASK; + mlxsw_reg_pmmp_eeprom_override_mask_set(pmmp_pl, eeprom_override_mask); + eeprom_override = low_power ? MLXSW_REG_PMMP_EEPROM_OVERRIDE_LOW_POWER_MASK : + 0; + mlxsw_reg_pmmp_eeprom_override_set(pmmp_pl, eeprom_override); + + return mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmmp), pmmp_pl); +} + +static int __mlxsw_env_set_module_power_mode(struct mlxsw_core *mlxsw_core, + u8 module, bool low_power, + struct netlink_ext_ack *extack) +{ + int err; + + err = mlxsw_env_module_enable_set(mlxsw_core, module, false); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Failed to disable module"); + return err; + } + + err = mlxsw_env_module_low_power_set(mlxsw_core, module, low_power); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Failed to set module's power mode"); + goto err_module_low_power_set; + } + + err = mlxsw_env_module_enable_set(mlxsw_core, module, true); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Failed to enable module"); + goto err_module_enable_set; + } + + return 0; + +err_module_enable_set: + mlxsw_env_module_low_power_set(mlxsw_core, module, !low_power); +err_module_low_power_set: + mlxsw_env_module_enable_set(mlxsw_core, module, true); + return err; +} + +int +mlxsw_env_set_module_power_mode(struct mlxsw_core *mlxsw_core, u8 module, + enum ethtool_module_power_mode_policy policy, + struct netlink_ext_ack *extack) +{ + struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core); + bool low_power; + int err = 0; + + if (WARN_ON_ONCE(module >= mlxsw_env->module_count)) + return -EINVAL; + + if (policy != ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH && + policy != ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO) { + NL_SET_ERR_MSG_MOD(extack, "Unsupported power mode policy"); + return -EOPNOTSUPP; + } + + mutex_lock(&mlxsw_env->module_info_lock); + + if (mlxsw_env->module_info[module].power_mode_policy == policy) + goto out; + + /* If any ports are up, we are already in high power mode. */ + if (mlxsw_env->module_info[module].num_ports_up) + goto out_set_policy; + + low_power = policy == ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO; + err = __mlxsw_env_set_module_power_mode(mlxsw_core, module, low_power, + extack); + if (err) + goto out; + +out_set_policy: + mlxsw_env->module_info[module].power_mode_policy = policy; +out: + mutex_unlock(&mlxsw_env->module_info_lock); + return err; +} +EXPORT_SYMBOL(mlxsw_env_set_module_power_mode); + static int mlxsw_env_module_has_temp_sensor(struct mlxsw_core *mlxsw_core, u8 module, bool *p_has_temp_sensor) @@ -482,22 +685,32 @@ static int mlxsw_env_module_temp_event_enable(struct mlxsw_core *mlxsw_core, return 0; } -static void mlxsw_env_mtwe_event_func(const struct mlxsw_reg_info *reg, - char *mtwe_pl, void *priv) +struct mlxsw_env_module_temp_warn_event { + struct mlxsw_env *mlxsw_env; + char mtwe_pl[MLXSW_REG_MTWE_LEN]; + struct work_struct work; +}; + +static void mlxsw_env_mtwe_event_work(struct work_struct *work) { - struct mlxsw_env *mlxsw_env = priv; + struct mlxsw_env_module_temp_warn_event *event; + struct mlxsw_env *mlxsw_env; int i, sensor_warning; bool is_overheat; + event = container_of(work, struct mlxsw_env_module_temp_warn_event, + work); + mlxsw_env = event->mlxsw_env; + for (i = 0; i < mlxsw_env->module_count; i++) { /* 64-127 of sensor_index are mapped to the port modules * sequentially (module 0 is mapped to sensor_index 64, * module 1 to sensor_index 65 and so on) */ sensor_warning = - mlxsw_reg_mtwe_sensor_warning_get(mtwe_pl, + mlxsw_reg_mtwe_sensor_warning_get(event->mtwe_pl, i + MLXSW_REG_MTMP_MODULE_INDEX_MIN); - spin_lock(&mlxsw_env->module_info_lock); + mutex_lock(&mlxsw_env->module_info_lock); is_overheat = mlxsw_env->module_info[i].is_overheat; @@ -507,13 +720,13 @@ static void mlxsw_env_mtwe_event_func(const struct mlxsw_reg_info *reg, * warning OR current state in "no warning" and MTWE * does not report warning. */ - spin_unlock(&mlxsw_env->module_info_lock); + mutex_unlock(&mlxsw_env->module_info_lock); continue; } else if (is_overheat && !sensor_warning) { /* MTWE reports "no warning", turn is_overheat off. */ mlxsw_env->module_info[i].is_overheat = false; - spin_unlock(&mlxsw_env->module_info_lock); + mutex_unlock(&mlxsw_env->module_info_lock); } else { /* Current state is "no warning" and MTWE reports * "warning", increase the counter and turn is_overheat @@ -521,13 +734,32 @@ static void mlxsw_env_mtwe_event_func(const struct mlxsw_reg_info *reg, */ mlxsw_env->module_info[i].is_overheat = true; mlxsw_env->module_info[i].module_overheat_counter++; - spin_unlock(&mlxsw_env->module_info_lock); + mutex_unlock(&mlxsw_env->module_info_lock); } } + + kfree(event); +} + +static void +mlxsw_env_mtwe_listener_func(const struct mlxsw_reg_info *reg, char *mtwe_pl, + void *priv) +{ + struct mlxsw_env_module_temp_warn_event *event; + struct mlxsw_env *mlxsw_env = priv; + + event = kmalloc(sizeof(*event), GFP_ATOMIC); + if (!event) + return; + + event->mlxsw_env = mlxsw_env; + memcpy(event->mtwe_pl, mtwe_pl, MLXSW_REG_MTWE_LEN); + INIT_WORK(&event->work, mlxsw_env_mtwe_event_work); + mlxsw_core_schedule_work(&event->work); } static const struct mlxsw_listener mlxsw_env_temp_warn_listener = - MLXSW_EVENTL(mlxsw_env_mtwe_event_func, MTWE, MTWE); + MLXSW_EVENTL(mlxsw_env_mtwe_listener_func, MTWE, MTWE); static int mlxsw_env_temp_warn_event_register(struct mlxsw_core *mlxsw_core) { @@ -568,9 +800,9 @@ static void mlxsw_env_pmpe_event_work(struct work_struct *work) work); mlxsw_env = event->mlxsw_env; - spin_lock_bh(&mlxsw_env->module_info_lock); + mutex_lock(&mlxsw_env->module_info_lock); mlxsw_env->module_info[event->module].is_overheat = false; - spin_unlock_bh(&mlxsw_env->module_info_lock); + mutex_unlock(&mlxsw_env->module_info_lock); err = mlxsw_env_module_has_temp_sensor(mlxsw_env->core, event->module, &has_temp_sensor); @@ -652,8 +884,10 @@ mlxsw_env_module_oper_state_event_enable(struct mlxsw_core *mlxsw_core, for (i = 0; i < module_count; i++) { char pmaos_pl[MLXSW_REG_PMAOS_LEN]; - mlxsw_reg_pmaos_pack(pmaos_pl, i, - MLXSW_REG_PMAOS_E_GENERATE_EVENT); + mlxsw_reg_pmaos_pack(pmaos_pl, i); + mlxsw_reg_pmaos_e_set(pmaos_pl, + MLXSW_REG_PMAOS_E_GENERATE_EVENT); + mlxsw_reg_pmaos_ee_set(pmaos_pl, true); err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), pmaos_pl); if (err) return err; @@ -667,29 +901,110 @@ mlxsw_env_module_overheat_counter_get(struct mlxsw_core *mlxsw_core, u8 module, { struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core); - /* Prevent switch driver from accessing uninitialized data. */ - if (!mlxsw_core_is_initialized(mlxsw_core)) { - *p_counter = 0; - return 0; - } - if (WARN_ON_ONCE(module >= mlxsw_env->module_count)) return -EINVAL; - spin_lock_bh(&mlxsw_env->module_info_lock); + mutex_lock(&mlxsw_env->module_info_lock); *p_counter = mlxsw_env->module_info[module].module_overheat_counter; - spin_unlock_bh(&mlxsw_env->module_info_lock); + mutex_unlock(&mlxsw_env->module_info_lock); return 0; } EXPORT_SYMBOL(mlxsw_env_module_overheat_counter_get); +void mlxsw_env_module_port_map(struct mlxsw_core *mlxsw_core, u8 module) +{ + struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core); + + if (WARN_ON_ONCE(module >= mlxsw_env->module_count)) + return; + + mutex_lock(&mlxsw_env->module_info_lock); + mlxsw_env->module_info[module].num_ports_mapped++; + mutex_unlock(&mlxsw_env->module_info_lock); +} +EXPORT_SYMBOL(mlxsw_env_module_port_map); + +void mlxsw_env_module_port_unmap(struct mlxsw_core *mlxsw_core, u8 module) +{ + struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core); + + if (WARN_ON_ONCE(module >= mlxsw_env->module_count)) + return; + + mutex_lock(&mlxsw_env->module_info_lock); + mlxsw_env->module_info[module].num_ports_mapped--; + mutex_unlock(&mlxsw_env->module_info_lock); +} +EXPORT_SYMBOL(mlxsw_env_module_port_unmap); + +int mlxsw_env_module_port_up(struct mlxsw_core *mlxsw_core, u8 module) +{ + struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core); + int err = 0; + + if (WARN_ON_ONCE(module >= mlxsw_env->module_count)) + return -EINVAL; + + mutex_lock(&mlxsw_env->module_info_lock); + + if (mlxsw_env->module_info[module].power_mode_policy != + ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO) + goto out_inc; + + if (mlxsw_env->module_info[module].num_ports_up != 0) + goto out_inc; + + /* Transition to high power mode following first port using the module + * being put administratively up. + */ + err = __mlxsw_env_set_module_power_mode(mlxsw_core, module, false, + NULL); + if (err) + goto out_unlock; + +out_inc: + mlxsw_env->module_info[module].num_ports_up++; +out_unlock: + mutex_unlock(&mlxsw_env->module_info_lock); + return err; +} +EXPORT_SYMBOL(mlxsw_env_module_port_up); + +void mlxsw_env_module_port_down(struct mlxsw_core *mlxsw_core, u8 module) +{ + struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core); + + if (WARN_ON_ONCE(module >= mlxsw_env->module_count)) + return; + + mutex_lock(&mlxsw_env->module_info_lock); + + mlxsw_env->module_info[module].num_ports_up--; + + if (mlxsw_env->module_info[module].power_mode_policy != + ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO) + goto out_unlock; + + if (mlxsw_env->module_info[module].num_ports_up != 0) + goto out_unlock; + + /* Transition to low power mode following last port using the module + * being put administratively down. + */ + __mlxsw_env_set_module_power_mode(mlxsw_core, module, true, NULL); + +out_unlock: + mutex_unlock(&mlxsw_env->module_info_lock); +} +EXPORT_SYMBOL(mlxsw_env_module_port_down); + int mlxsw_env_init(struct mlxsw_core *mlxsw_core, struct mlxsw_env **p_env) { char mgpir_pl[MLXSW_REG_MGPIR_LEN]; struct mlxsw_env *env; u8 module_count; - int err; + int i, err; mlxsw_reg_mgpir_pack(mgpir_pl); err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgpir), mgpir_pl); @@ -702,7 +1017,14 @@ int mlxsw_env_init(struct mlxsw_core *mlxsw_core, struct mlxsw_env **p_env) if (!env) return -ENOMEM; - spin_lock_init(&env->module_info_lock); + /* Firmware defaults to high power mode policy where modules are + * transitioned to high power mode following plug-in. + */ + for (i = 0; i < module_count; i++) + env->module_info[i].power_mode_policy = + ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH; + + mutex_init(&env->module_info_lock); env->core = mlxsw_core; env->module_count = module_count; *p_env = env; @@ -732,6 +1054,7 @@ err_oper_state_event_enable: err_module_plug_event_register: mlxsw_env_temp_warn_event_unregister(env); err_temp_warn_event_register: + mutex_destroy(&env->module_info_lock); kfree(env); return err; } @@ -742,5 +1065,6 @@ void mlxsw_env_fini(struct mlxsw_env *env) /* Make sure there is no more event work scheduled. */ mlxsw_core_flush_owq(); mlxsw_env_temp_warn_event_unregister(env); + mutex_destroy(&env->module_info_lock); kfree(env); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_env.h b/drivers/net/ethernet/mellanox/mlxsw/core_env.h index 0bf5bd0f8a7e..da121b1a84b4 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_env.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core_env.h @@ -24,9 +24,32 @@ mlxsw_env_get_module_eeprom_by_page(struct mlxsw_core *mlxsw_core, u8 module, const struct ethtool_module_eeprom *page, struct netlink_ext_ack *extack); +int mlxsw_env_reset_module(struct net_device *netdev, + struct mlxsw_core *mlxsw_core, u8 module, + u32 *flags); + +int +mlxsw_env_get_module_power_mode(struct mlxsw_core *mlxsw_core, u8 module, + struct ethtool_module_power_mode_params *params, + struct netlink_ext_ack *extack); + +int +mlxsw_env_set_module_power_mode(struct mlxsw_core *mlxsw_core, u8 module, + enum ethtool_module_power_mode_policy policy, + struct netlink_ext_ack *extack); + int mlxsw_env_module_overheat_counter_get(struct mlxsw_core *mlxsw_core, u8 module, u64 *p_counter); + +void mlxsw_env_module_port_map(struct mlxsw_core *mlxsw_core, u8 module); + +void mlxsw_env_module_port_unmap(struct mlxsw_core *mlxsw_core, u8 module); + +int mlxsw_env_module_port_up(struct mlxsw_core *mlxsw_core, u8 module); + +void mlxsw_env_module_port_down(struct mlxsw_core *mlxsw_core, u8 module); + int mlxsw_env_init(struct mlxsw_core *core, struct mlxsw_env **p_env); void mlxsw_env_fini(struct mlxsw_env *env); diff --git a/drivers/net/ethernet/mellanox/mlxsw/item.h b/drivers/net/ethernet/mellanox/mlxsw/item.h index e92cadc98128..ab70a873a01a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/item.h +++ b/drivers/net/ethernet/mellanox/mlxsw/item.h @@ -270,11 +270,13 @@ static struct mlxsw_item __ITEM_NAME(_type, _cname, _iname) = { \ .size = {.bits = _sizebits,}, \ .name = #_type "_" #_cname "_" #_iname, \ }; \ -static inline u8 mlxsw_##_type##_##_cname##_##_iname##_get(const char *buf) \ +static inline u8 __maybe_unused \ +mlxsw_##_type##_##_cname##_##_iname##_get(const char *buf) \ { \ return __mlxsw_item_get8(buf, &__ITEM_NAME(_type, _cname, _iname), 0); \ } \ -static inline void mlxsw_##_type##_##_cname##_##_iname##_set(char *buf, u8 val)\ +static inline void __maybe_unused \ +mlxsw_##_type##_##_cname##_##_iname##_set(char *buf, u8 val) \ { \ __mlxsw_item_set8(buf, &__ITEM_NAME(_type, _cname, _iname), 0, val); \ } @@ -290,13 +292,13 @@ static struct mlxsw_item __ITEM_NAME(_type, _cname, _iname) = { \ .size = {.bits = _sizebits,}, \ .name = #_type "_" #_cname "_" #_iname, \ }; \ -static inline u8 \ +static inline u8 __maybe_unused \ mlxsw_##_type##_##_cname##_##_iname##_get(const char *buf, unsigned short index)\ { \ return __mlxsw_item_get8(buf, &__ITEM_NAME(_type, _cname, _iname), \ index); \ } \ -static inline void \ +static inline void __maybe_unused \ mlxsw_##_type##_##_cname##_##_iname##_set(char *buf, unsigned short index, \ u8 val) \ { \ @@ -311,11 +313,13 @@ static struct mlxsw_item __ITEM_NAME(_type, _cname, _iname) = { \ .size = {.bits = _sizebits,}, \ .name = #_type "_" #_cname "_" #_iname, \ }; \ -static inline u16 mlxsw_##_type##_##_cname##_##_iname##_get(const char *buf) \ +static inline u16 __maybe_unused \ +mlxsw_##_type##_##_cname##_##_iname##_get(const char *buf) \ { \ return __mlxsw_item_get16(buf, &__ITEM_NAME(_type, _cname, _iname), 0); \ } \ -static inline void mlxsw_##_type##_##_cname##_##_iname##_set(char *buf, u16 val)\ +static inline void __maybe_unused \ +mlxsw_##_type##_##_cname##_##_iname##_set(char *buf, u16 val) \ { \ __mlxsw_item_set16(buf, &__ITEM_NAME(_type, _cname, _iname), 0, val); \ } @@ -331,13 +335,13 @@ static struct mlxsw_item __ITEM_NAME(_type, _cname, _iname) = { \ .size = {.bits = _sizebits,}, \ .name = #_type "_" #_cname "_" #_iname, \ }; \ -static inline u16 \ +static inline u16 __maybe_unused \ mlxsw_##_type##_##_cname##_##_iname##_get(const char *buf, unsigned short index)\ { \ return __mlxsw_item_get16(buf, &__ITEM_NAME(_type, _cname, _iname), \ index); \ } \ -static inline void \ +static inline void __maybe_unused \ mlxsw_##_type##_##_cname##_##_iname##_set(char *buf, unsigned short index, \ u16 val) \ { \ @@ -352,11 +356,13 @@ static struct mlxsw_item __ITEM_NAME(_type, _cname, _iname) = { \ .size = {.bits = _sizebits,}, \ .name = #_type "_" #_cname "_" #_iname, \ }; \ -static inline u32 mlxsw_##_type##_##_cname##_##_iname##_get(const char *buf) \ +static inline u32 __maybe_unused \ +mlxsw_##_type##_##_cname##_##_iname##_get(const char *buf) \ { \ return __mlxsw_item_get32(buf, &__ITEM_NAME(_type, _cname, _iname), 0); \ } \ -static inline void mlxsw_##_type##_##_cname##_##_iname##_set(char *buf, u32 val)\ +static inline void __maybe_unused \ +mlxsw_##_type##_##_cname##_##_iname##_set(char *buf, u32 val) \ { \ __mlxsw_item_set32(buf, &__ITEM_NAME(_type, _cname, _iname), 0, val); \ } @@ -372,13 +378,13 @@ static struct mlxsw_item __ITEM_NAME(_type, _cname, _iname) = { \ .size = {.bits = _sizebits,}, \ .name = #_type "_" #_cname "_" #_iname, \ }; \ -static inline u32 \ +static inline u32 __maybe_unused \ mlxsw_##_type##_##_cname##_##_iname##_get(const char *buf, unsigned short index)\ { \ return __mlxsw_item_get32(buf, &__ITEM_NAME(_type, _cname, _iname), \ index); \ } \ -static inline void \ +static inline void __maybe_unused \ mlxsw_##_type##_##_cname##_##_iname##_set(char *buf, unsigned short index, \ u32 val) \ { \ @@ -393,11 +399,13 @@ static struct mlxsw_item __ITEM_NAME(_type, _cname, _iname) = { \ .size = {.bits = _sizebits,}, \ .name = #_type "_" #_cname "_" #_iname, \ }; \ -static inline u64 mlxsw_##_type##_##_cname##_##_iname##_get(const char *buf) \ +static inline u64 __maybe_unused \ +mlxsw_##_type##_##_cname##_##_iname##_get(const char *buf) \ { \ return __mlxsw_item_get64(buf, &__ITEM_NAME(_type, _cname, _iname), 0); \ } \ -static inline void mlxsw_##_type##_##_cname##_##_iname##_set(char *buf, u64 val)\ +static inline void __maybe_unused \ +mlxsw_##_type##_##_cname##_##_iname##_set(char *buf, u64 val) \ { \ __mlxsw_item_set64(buf, &__ITEM_NAME(_type, _cname, _iname), 0, val); \ } @@ -413,13 +421,13 @@ static struct mlxsw_item __ITEM_NAME(_type, _cname, _iname) = { \ .size = {.bits = _sizebits,}, \ .name = #_type "_" #_cname "_" #_iname, \ }; \ -static inline u64 \ +static inline u64 __maybe_unused \ mlxsw_##_type##_##_cname##_##_iname##_get(const char *buf, unsigned short index)\ { \ return __mlxsw_item_get64(buf, &__ITEM_NAME(_type, _cname, _iname), \ index); \ } \ -static inline void \ +static inline void __maybe_unused \ mlxsw_##_type##_##_cname##_##_iname##_set(char *buf, unsigned short index, \ u64 val) \ { \ @@ -433,19 +441,19 @@ static struct mlxsw_item __ITEM_NAME(_type, _cname, _iname) = { \ .size = {.bytes = _sizebytes,}, \ .name = #_type "_" #_cname "_" #_iname, \ }; \ -static inline void \ +static inline void __maybe_unused \ mlxsw_##_type##_##_cname##_##_iname##_memcpy_from(const char *buf, char *dst) \ { \ __mlxsw_item_memcpy_from(buf, dst, \ &__ITEM_NAME(_type, _cname, _iname), 0); \ } \ -static inline void \ +static inline void __maybe_unused \ mlxsw_##_type##_##_cname##_##_iname##_memcpy_to(char *buf, const char *src) \ { \ __mlxsw_item_memcpy_to(buf, src, \ &__ITEM_NAME(_type, _cname, _iname), 0); \ } \ -static inline char * \ +static inline char * __maybe_unused \ mlxsw_##_type##_##_cname##_##_iname##_data(char *buf) \ { \ return __mlxsw_item_data(buf, &__ITEM_NAME(_type, _cname, _iname), 0); \ @@ -460,7 +468,7 @@ static struct mlxsw_item __ITEM_NAME(_type, _cname, _iname) = { \ .size = {.bytes = _sizebytes,}, \ .name = #_type "_" #_cname "_" #_iname, \ }; \ -static inline void \ +static inline void __maybe_unused \ mlxsw_##_type##_##_cname##_##_iname##_memcpy_from(const char *buf, \ unsigned short index, \ char *dst) \ @@ -468,7 +476,7 @@ mlxsw_##_type##_##_cname##_##_iname##_memcpy_from(const char *buf, \ __mlxsw_item_memcpy_from(buf, dst, \ &__ITEM_NAME(_type, _cname, _iname), index); \ } \ -static inline void \ +static inline void __maybe_unused \ mlxsw_##_type##_##_cname##_##_iname##_memcpy_to(char *buf, \ unsigned short index, \ const char *src) \ @@ -476,7 +484,7 @@ mlxsw_##_type##_##_cname##_##_iname##_memcpy_to(char *buf, \ __mlxsw_item_memcpy_to(buf, src, \ &__ITEM_NAME(_type, _cname, _iname), index); \ } \ -static inline char * \ +static inline char * __maybe_unused \ mlxsw_##_type##_##_cname##_##_iname##_data(char *buf, unsigned short index) \ { \ return __mlxsw_item_data(buf, \ @@ -491,14 +499,14 @@ static struct mlxsw_item __ITEM_NAME(_type, _cname, _iname) = { \ .size = {.bytes = _sizebytes,}, \ .name = #_type "_" #_cname "_" #_iname, \ }; \ -static inline u8 \ +static inline u8 __maybe_unused \ mlxsw_##_type##_##_cname##_##_iname##_get(const char *buf, u16 index) \ { \ return __mlxsw_item_bit_array_get(buf, \ &__ITEM_NAME(_type, _cname, _iname), \ index); \ } \ -static inline void \ +static inline void __maybe_unused \ mlxsw_##_type##_##_cname##_##_iname##_set(char *buf, u16 index, u8 val) \ { \ return __mlxsw_item_bit_array_set(buf, \ diff --git a/drivers/net/ethernet/mellanox/mlxsw/minimal.c b/drivers/net/ethernet/mellanox/mlxsw/minimal.c index d9d56c44e994..5d4dfa5ddbb5 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/minimal.c +++ b/drivers/net/ethernet/mellanox/mlxsw/minimal.c @@ -54,8 +54,20 @@ static int mlxsw_m_base_mac_get(struct mlxsw_m *mlxsw_m) return 0; } -static int mlxsw_m_port_dummy_open_stop(struct net_device *dev) +static int mlxsw_m_port_open(struct net_device *dev) { + struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev); + struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m; + + return mlxsw_env_module_port_up(mlxsw_m->core, mlxsw_m_port->module); +} + +static int mlxsw_m_port_stop(struct net_device *dev) +{ + struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev); + struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m; + + mlxsw_env_module_port_down(mlxsw_m->core, mlxsw_m_port->module); return 0; } @@ -70,8 +82,8 @@ mlxsw_m_port_get_devlink_port(struct net_device *dev) } static const struct net_device_ops mlxsw_m_port_netdev_ops = { - .ndo_open = mlxsw_m_port_dummy_open_stop, - .ndo_stop = mlxsw_m_port_dummy_open_stop, + .ndo_open = mlxsw_m_port_open, + .ndo_stop = mlxsw_m_port_stop, .ndo_get_devlink_port = mlxsw_m_port_get_devlink_port, }; @@ -124,11 +136,47 @@ mlxsw_m_get_module_eeprom_by_page(struct net_device *netdev, page, extack); } +static int mlxsw_m_reset(struct net_device *netdev, u32 *flags) +{ + struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev); + struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core; + + return mlxsw_env_reset_module(netdev, core, mlxsw_m_port->module, + flags); +} + +static int +mlxsw_m_get_module_power_mode(struct net_device *netdev, + struct ethtool_module_power_mode_params *params, + struct netlink_ext_ack *extack) +{ + struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev); + struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core; + + return mlxsw_env_get_module_power_mode(core, mlxsw_m_port->module, + params, extack); +} + +static int +mlxsw_m_set_module_power_mode(struct net_device *netdev, + const struct ethtool_module_power_mode_params *params, + struct netlink_ext_ack *extack) +{ + struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev); + struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core; + + return mlxsw_env_set_module_power_mode(core, mlxsw_m_port->module, + params->policy, extack); +} + static const struct ethtool_ops mlxsw_m_port_ethtool_ops = { .get_drvinfo = mlxsw_m_module_get_drvinfo, .get_module_info = mlxsw_m_get_module_info, .get_module_eeprom = mlxsw_m_get_module_eeprom, .get_module_eeprom_by_page = mlxsw_m_get_module_eeprom_by_page, + .reset = mlxsw_m_reset, + .get_module_power_mode = mlxsw_m_get_module_power_mode, + .set_module_power_mode = mlxsw_m_set_module_power_mode, }; static int @@ -152,20 +200,16 @@ static int mlxsw_m_port_dev_addr_get(struct mlxsw_m_port *mlxsw_m_port) { struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m; - struct net_device *dev = mlxsw_m_port->dev; char ppad_pl[MLXSW_REG_PPAD_LEN]; + u8 addr[ETH_ALEN]; int err; mlxsw_reg_ppad_pack(ppad_pl, false, 0); err = mlxsw_reg_query(mlxsw_m->core, MLXSW_REG(ppad), ppad_pl); if (err) return err; - mlxsw_reg_ppad_mac_memcpy_from(ppad_pl, dev->dev_addr); - /* The last byte value in base mac address is guaranteed - * to be such it does not overflow when adding local_port - * value. - */ - dev->dev_addr[ETH_ALEN - 1] += mlxsw_m_port->module + 1; + mlxsw_reg_ppad_mac_memcpy_from(ppad_pl, addr); + eth_hw_addr_gen(mlxsw_m_port->dev, addr, mlxsw_m_port->module + 1); return 0; } @@ -266,6 +310,7 @@ static int mlxsw_m_port_module_map(struct mlxsw_m *mlxsw_m, u8 local_port, if (WARN_ON_ONCE(module >= max_ports)) return -EINVAL; + mlxsw_env_module_port_map(mlxsw_m->core, module); mlxsw_m->module_to_port[module] = ++mlxsw_m->max_ports; return 0; @@ -274,6 +319,7 @@ static int mlxsw_m_port_module_map(struct mlxsw_m *mlxsw_m, u8 local_port, static void mlxsw_m_port_module_unmap(struct mlxsw_m *mlxsw_m, u8 module) { mlxsw_m->module_to_port[module] = -1; + mlxsw_env_module_port_unmap(mlxsw_m->core, module); } static int mlxsw_m_ports_create(struct mlxsw_m *mlxsw_m) diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 6fbda6ebd590..8d420eb8ade2 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -4951,7 +4951,7 @@ enum mlxsw_reg_ppcnt_grp { MLXSW_REG_PPCNT_DISCARD_CNT = 0x6, MLXSW_REG_PPCNT_PRIO_CNT = 0x10, MLXSW_REG_PPCNT_TC_CNT = 0x11, - MLXSW_REG_PPCNT_TC_CONG_TC = 0x13, + MLXSW_REG_PPCNT_TC_CONG_CNT = 0x13, }; /* reg_ppcnt_grp @@ -5371,7 +5371,7 @@ MLXSW_ITEM64(reg, ppcnt, tx_pause_duration, MLXSW_ITEM64(reg, ppcnt, tx_pause_transition, MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x70, 0, 64); -/* Ethernet Per Traffic Group Counters */ +/* Ethernet Per Traffic Class Counters */ /* reg_ppcnt_tc_transmit_queue * Contains the transmit queue depth in cells of traffic class @@ -5398,6 +5398,12 @@ MLXSW_ITEM64(reg, ppcnt, tc_no_buffer_discard_uc, MLXSW_ITEM64(reg, ppcnt, wred_discard, MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x00, 0, 64); +/* reg_ppcnt_ecn_marked_tc + * Access: RO + */ +MLXSW_ITEM64(reg, ppcnt, ecn_marked_tc, + MLXSW_REG_PPCNT_COUNTERS_OFFSET + 0x08, 0, 64); + static inline void mlxsw_reg_ppcnt_pack(char *payload, u8 local_port, enum mlxsw_reg_ppcnt_grp grp, u8 prio_tc) @@ -5681,6 +5687,14 @@ static inline void mlxsw_reg_pspa_pack(char *payload, u8 swid, u8 local_port) MLXSW_REG_DEFINE(pmaos, MLXSW_REG_PMAOS_ID, MLXSW_REG_PMAOS_LEN); +/* reg_pmaos_rst + * Module reset toggle. + * Note: Setting reset while module is plugged-in will result in transition to + * "initializing" operational state. + * Access: OP + */ +MLXSW_ITEM32(reg, pmaos, rst, 0x00, 31, 1); + /* reg_pmaos_slot_index * Slot index. * Access: Index @@ -5693,6 +5707,24 @@ MLXSW_ITEM32(reg, pmaos, slot_index, 0x00, 24, 4); */ MLXSW_ITEM32(reg, pmaos, module, 0x00, 16, 8); +enum mlxsw_reg_pmaos_admin_status { + MLXSW_REG_PMAOS_ADMIN_STATUS_ENABLED = 1, + MLXSW_REG_PMAOS_ADMIN_STATUS_DISABLED = 2, + /* If the module is active and then unplugged, or experienced an error + * event, the operational status should go to "disabled" and can only + * be enabled upon explicit enable command. + */ + MLXSW_REG_PMAOS_ADMIN_STATUS_ENABLED_ONCE = 3, +}; + +/* reg_pmaos_admin_status + * Module administrative state (the desired state of the module). + * Note: To disable a module, all ports associated with the port must be + * administatively down first. + * Access: RW + */ +MLXSW_ITEM32(reg, pmaos, admin_status, 0x00, 8, 4); + /* reg_pmaos_ase * Admin state update enable. * If this bit is set, admin state will be updated based on admin_state field. @@ -5721,13 +5753,10 @@ enum mlxsw_reg_pmaos_e { */ MLXSW_ITEM32(reg, pmaos, e, 0x04, 0, 2); -static inline void mlxsw_reg_pmaos_pack(char *payload, u8 module, - enum mlxsw_reg_pmaos_e e) +static inline void mlxsw_reg_pmaos_pack(char *payload, u8 module) { MLXSW_REG_ZERO(pmaos, payload); mlxsw_reg_pmaos_module_set(payload, module); - mlxsw_reg_pmaos_e_set(payload, e); - mlxsw_reg_pmaos_ee_set(payload, true); } /* PPLR - Port Physical Loopback Register @@ -5766,6 +5795,69 @@ static inline void mlxsw_reg_pplr_pack(char *payload, u8 local_port, MLXSW_REG_PPLR_LB_TYPE_BIT_PHY_LOCAL : 0); } +/* PMTDB - Port Module To local DataBase Register + * ---------------------------------------------- + * The PMTDB register allows to query the possible module<->local port + * mapping than can be used in PMLP. It does not represent the actual/current + * mapping of the local to module. Actual mapping is only defined by PMLP. + */ +#define MLXSW_REG_PMTDB_ID 0x501A +#define MLXSW_REG_PMTDB_LEN 0x40 + +MLXSW_REG_DEFINE(pmtdb, MLXSW_REG_PMTDB_ID, MLXSW_REG_PMTDB_LEN); + +/* reg_pmtdb_slot_index + * Slot index (0: Main board). + * Access: Index + */ +MLXSW_ITEM32(reg, pmtdb, slot_index, 0x00, 24, 4); + +/* reg_pmtdb_module + * Module number. + * Access: Index + */ +MLXSW_ITEM32(reg, pmtdb, module, 0x00, 16, 8); + +/* reg_pmtdb_ports_width + * Port's width + * Access: Index + */ +MLXSW_ITEM32(reg, pmtdb, ports_width, 0x00, 12, 4); + +/* reg_pmtdb_num_ports + * Number of ports in a single module (split/breakout) + * Access: Index + */ +MLXSW_ITEM32(reg, pmtdb, num_ports, 0x00, 8, 4); + +enum mlxsw_reg_pmtdb_status { + MLXSW_REG_PMTDB_STATUS_SUCCESS, +}; + +/* reg_pmtdb_status + * Status + * Access: RO + */ +MLXSW_ITEM32(reg, pmtdb, status, 0x00, 0, 4); + +/* reg_pmtdb_port_num + * The local_port value which can be assigned to the module. + * In case of more than one port, port<x> represent the /<x> port of + * the module. + * Access: RO + */ +MLXSW_ITEM16_INDEXED(reg, pmtdb, port_num, 0x04, 0, 8, 0x02, 0x00, false); + +static inline void mlxsw_reg_pmtdb_pack(char *payload, u8 slot_index, u8 module, + u8 ports_width, u8 num_ports) +{ + MLXSW_REG_ZERO(pmtdb, payload); + mlxsw_reg_pmtdb_slot_index_set(payload, slot_index); + mlxsw_reg_pmtdb_module_set(payload, module); + mlxsw_reg_pmtdb_ports_width_set(payload, ports_width); + mlxsw_reg_pmtdb_num_ports_set(payload, num_ports); +} + /* PMPE - Port Module Plug/Unplug Event Register * --------------------------------------------- * This register reports any operational status change of a module. @@ -5860,67 +5952,100 @@ static inline void mlxsw_reg_pddr_pack(char *payload, u8 local_port, mlxsw_reg_pddr_page_select_set(payload, page_select); } -/* PMTM - Port Module Type Mapping Register - * ---------------------------------------- - * The PMTM allows query or configuration of module types. +/* PMMP - Port Module Memory Map Properties Register + * ------------------------------------------------- + * The PMMP register allows to override the module memory map advertisement. + * The register can only be set when the module is disabled by PMAOS register. */ -#define MLXSW_REG_PMTM_ID 0x5067 -#define MLXSW_REG_PMTM_LEN 0x10 +#define MLXSW_REG_PMMP_ID 0x5044 +#define MLXSW_REG_PMMP_LEN 0x2C -MLXSW_REG_DEFINE(pmtm, MLXSW_REG_PMTM_ID, MLXSW_REG_PMTM_LEN); +MLXSW_REG_DEFINE(pmmp, MLXSW_REG_PMMP_ID, MLXSW_REG_PMMP_LEN); -/* reg_pmtm_module +/* reg_pmmp_module * Module number. * Access: Index */ -MLXSW_ITEM32(reg, pmtm, module, 0x00, 16, 8); +MLXSW_ITEM32(reg, pmmp, module, 0x00, 16, 8); -enum mlxsw_reg_pmtm_module_type { - /* Backplane with 4 lanes */ - MLXSW_REG_PMTM_MODULE_TYPE_BP_4X, - /* QSFP */ - MLXSW_REG_PMTM_MODULE_TYPE_QSFP, - /* SFP */ - MLXSW_REG_PMTM_MODULE_TYPE_SFP, - /* Backplane with single lane */ - MLXSW_REG_PMTM_MODULE_TYPE_BP_1X = 4, - /* Backplane with two lane */ - MLXSW_REG_PMTM_MODULE_TYPE_BP_2X = 8, - /* Chip2Chip4x */ - MLXSW_REG_PMTM_MODULE_TYPE_C2C4X = 10, - /* Chip2Chip2x */ - MLXSW_REG_PMTM_MODULE_TYPE_C2C2X, - /* Chip2Chip1x */ - MLXSW_REG_PMTM_MODULE_TYPE_C2C1X, - /* QSFP-DD */ - MLXSW_REG_PMTM_MODULE_TYPE_QSFP_DD = 14, - /* OSFP */ - MLXSW_REG_PMTM_MODULE_TYPE_OSFP, - /* SFP-DD */ - MLXSW_REG_PMTM_MODULE_TYPE_SFP_DD, - /* DSFP */ - MLXSW_REG_PMTM_MODULE_TYPE_DSFP, - /* Chip2Chip8x */ - MLXSW_REG_PMTM_MODULE_TYPE_C2C8X, +/* reg_pmmp_sticky + * When set, will keep eeprom_override values after plug-out event. + * Access: OP + */ +MLXSW_ITEM32(reg, pmmp, sticky, 0x00, 0, 1); + +/* reg_pmmp_eeprom_override_mask + * Write mask bit (negative polarity). + * 0 - Allow write + * 1 - Ignore write + * On write, indicates which of the bits from eeprom_override field are + * updated. + * Access: WO + */ +MLXSW_ITEM32(reg, pmmp, eeprom_override_mask, 0x04, 16, 16); + +enum { + /* Set module to low power mode */ + MLXSW_REG_PMMP_EEPROM_OVERRIDE_LOW_POWER_MASK = BIT(8), }; -/* reg_pmtm_module_type - * Module type. +/* reg_pmmp_eeprom_override + * Override / ignore EEPROM advertisement properties bitmask * Access: RW */ -MLXSW_ITEM32(reg, pmtm, module_type, 0x04, 0, 4); +MLXSW_ITEM32(reg, pmmp, eeprom_override, 0x04, 0, 16); -static inline void mlxsw_reg_pmtm_pack(char *payload, u8 module) +static inline void mlxsw_reg_pmmp_pack(char *payload, u8 module) { - MLXSW_REG_ZERO(pmtm, payload); - mlxsw_reg_pmtm_module_set(payload, module); + MLXSW_REG_ZERO(pmmp, payload); + mlxsw_reg_pmmp_module_set(payload, module); } -static inline void -mlxsw_reg_pmtm_unpack(char *payload, - enum mlxsw_reg_pmtm_module_type *module_type) +/* PLLP - Port Local port to Label Port mapping Register + * ----------------------------------------------------- + * The PLLP register returns the mapping from Local Port into Label Port. + */ +#define MLXSW_REG_PLLP_ID 0x504A +#define MLXSW_REG_PLLP_LEN 0x10 + +MLXSW_REG_DEFINE(pllp, MLXSW_REG_PLLP_ID, MLXSW_REG_PLLP_LEN); + +/* reg_pllp_local_port + * Local port number. + * Access: Index + */ +MLXSW_ITEM32(reg, pllp, local_port, 0x00, 16, 8); + +/* reg_pllp_label_port + * Front panel label of the port. + * Access: RO + */ +MLXSW_ITEM32(reg, pllp, label_port, 0x00, 0, 8); + +/* reg_pllp_split_num + * Label split mapping for local_port. + * Access: RO + */ +MLXSW_ITEM32(reg, pllp, split_num, 0x04, 0, 4); + +/* reg_pllp_slot_index + * Slot index (0: Main board). + * Access: RO + */ +MLXSW_ITEM32(reg, pllp, slot_index, 0x08, 0, 4); + +static inline void mlxsw_reg_pllp_pack(char *payload, u8 local_port) { - *module_type = mlxsw_reg_pmtm_module_type_get(payload); + MLXSW_REG_ZERO(pllp, payload); + mlxsw_reg_pllp_local_port_set(payload, local_port); +} + +static inline void mlxsw_reg_pllp_unpack(char *payload, u8 *label_port, + u8 *split_num, u8 *slot_index) +{ + *label_port = mlxsw_reg_pllp_label_port_get(payload); + *split_num = mlxsw_reg_pllp_split_num_get(payload); + *slot_index = mlxsw_reg_pllp_slot_index_get(payload); } /* HTGT - Host Trap Group Table @@ -6401,6 +6526,12 @@ MLXSW_ITEM32(reg, ritr, mtu, 0x34, 0, 16); */ MLXSW_ITEM32(reg, ritr, if_swid, 0x08, 24, 8); +/* reg_ritr_if_mac_profile_id + * MAC msb profile ID. + * Access: RW + */ +MLXSW_ITEM32(reg, ritr, if_mac_profile_id, 0x10, 16, 4); + /* reg_ritr_if_mac * Router interface MAC address. * In Spectrum, all MAC addresses must have the same 38 MSBits. @@ -6664,6 +6795,23 @@ mlxsw_reg_ritr_loopback_ipip4_pack(char *payload, mlxsw_reg_ritr_loopback_ipip_usip4_set(payload, usip); } +static inline void +mlxsw_reg_ritr_loopback_ipip6_pack(char *payload, + enum mlxsw_reg_ritr_loopback_ipip_type ipip_type, + enum mlxsw_reg_ritr_loopback_ipip_options options, + u16 uvr_id, u16 underlay_rif, + const struct in6_addr *usip, u32 gre_key) +{ + enum mlxsw_reg_ritr_loopback_protocol protocol = + MLXSW_REG_RITR_LOOPBACK_PROTOCOL_IPIP_IPV6; + + mlxsw_reg_ritr_loopback_protocol_set(payload, protocol); + mlxsw_reg_ritr_loopback_ipip_common_pack(payload, ipip_type, options, + uvr_id, underlay_rif, gre_key); + mlxsw_reg_ritr_loopback_ipip_usip6_memcpy_to(payload, + (const char *)usip); +} + /* RTAR - Router TCAM Allocation Register * -------------------------------------- * This register is used for allocation of regions in the TCAM table. @@ -6932,6 +7080,12 @@ static inline void mlxsw_reg_ratr_ipip4_entry_pack(char *payload, u32 ipv4_udip) mlxsw_reg_ratr_ipip_ipv4_udip_set(payload, ipv4_udip); } +static inline void mlxsw_reg_ratr_ipip6_entry_pack(char *payload, u32 ipv6_ptr) +{ + mlxsw_reg_ratr_ipip_type_set(payload, MLXSW_REG_RATR_IPIP_TYPE_IPV6); + mlxsw_reg_ratr_ipip_ipv6_ptr_set(payload, ipv6_ptr); +} + static inline void mlxsw_reg_ratr_counter_pack(char *payload, u64 counter_index, bool counter_enable) { @@ -8117,19 +8271,71 @@ static inline void mlxsw_reg_rtdp_pack(char *payload, } static inline void -mlxsw_reg_rtdp_ipip4_pack(char *payload, u16 irif, - enum mlxsw_reg_rtdp_ipip_sip_check sip_check, - unsigned int type_check, bool gre_key_check, - u32 ipv4_usip, u32 expected_gre_key) +mlxsw_reg_rtdp_ipip_pack(char *payload, u16 irif, + enum mlxsw_reg_rtdp_ipip_sip_check sip_check, + unsigned int type_check, bool gre_key_check, + u32 expected_gre_key) { mlxsw_reg_rtdp_ipip_irif_set(payload, irif); mlxsw_reg_rtdp_ipip_sip_check_set(payload, sip_check); mlxsw_reg_rtdp_ipip_type_check_set(payload, type_check); mlxsw_reg_rtdp_ipip_gre_key_check_set(payload, gre_key_check); - mlxsw_reg_rtdp_ipip_ipv4_usip_set(payload, ipv4_usip); mlxsw_reg_rtdp_ipip_expected_gre_key_set(payload, expected_gre_key); } +static inline void +mlxsw_reg_rtdp_ipip4_pack(char *payload, u16 irif, + enum mlxsw_reg_rtdp_ipip_sip_check sip_check, + unsigned int type_check, bool gre_key_check, + u32 ipv4_usip, u32 expected_gre_key) +{ + mlxsw_reg_rtdp_ipip_pack(payload, irif, sip_check, type_check, + gre_key_check, expected_gre_key); + mlxsw_reg_rtdp_ipip_ipv4_usip_set(payload, ipv4_usip); +} + +static inline void +mlxsw_reg_rtdp_ipip6_pack(char *payload, u16 irif, + enum mlxsw_reg_rtdp_ipip_sip_check sip_check, + unsigned int type_check, bool gre_key_check, + u32 ipv6_usip_ptr, u32 expected_gre_key) +{ + mlxsw_reg_rtdp_ipip_pack(payload, irif, sip_check, type_check, + gre_key_check, expected_gre_key); + mlxsw_reg_rtdp_ipip_ipv6_usip_ptr_set(payload, ipv6_usip_ptr); +} + +/* RIPS - Router IP version Six Register + * ------------------------------------- + * The RIPS register is used to store IPv6 addresses for use by the NVE and + * IPinIP + */ +#define MLXSW_REG_RIPS_ID 0x8021 +#define MLXSW_REG_RIPS_LEN 0x14 + +MLXSW_REG_DEFINE(rips, MLXSW_REG_RIPS_ID, MLXSW_REG_RIPS_LEN); + +/* reg_rips_index + * Index to IPv6 address. + * For Spectrum, the index is to the KVD linear. + * Access: Index + */ +MLXSW_ITEM32(reg, rips, index, 0x00, 0, 24); + +/* reg_rips_ipv6 + * IPv6 address + * Access: RW + */ +MLXSW_ITEM_BUF(reg, rips, ipv6, 0x04, 16); + +static inline void mlxsw_reg_rips_pack(char *payload, u32 index, + const struct in6_addr *ipv6) +{ + MLXSW_REG_ZERO(rips, payload); + mlxsw_reg_rips_index_set(payload, index); + mlxsw_reg_rips_ipv6_memcpy_to(payload, (const char *)ipv6); +} + /* RATRAD - Router Adjacency Table Activity Dump Register * ------------------------------------------------------ * The RATRAD register is used to dump and optionally clear activity bits of @@ -10208,6 +10414,39 @@ static inline void mlxsw_reg_mlcr_pack(char *payload, u8 local_port, MLXSW_REG_MLCR_DURATION_MAX : 0); } +/* MCION - Management Cable IO and Notifications Register + * ------------------------------------------------------ + * The MCION register is used to query transceiver modules' IO pins and other + * notifications. + */ +#define MLXSW_REG_MCION_ID 0x9052 +#define MLXSW_REG_MCION_LEN 0x18 + +MLXSW_REG_DEFINE(mcion, MLXSW_REG_MCION_ID, MLXSW_REG_MCION_LEN); + +/* reg_mcion_module + * Module number. + * Access: Index + */ +MLXSW_ITEM32(reg, mcion, module, 0x00, 16, 8); + +enum { + MLXSW_REG_MCION_MODULE_STATUS_BITS_PRESENT_MASK = BIT(0), + MLXSW_REG_MCION_MODULE_STATUS_BITS_LOW_POWER_MASK = BIT(8), +}; + +/* reg_mcion_module_status_bits + * Module IO status as defined by SFF. + * Access: RO + */ +MLXSW_ITEM32(reg, mcion, module_status_bits, 0x04, 0, 16); + +static inline void mlxsw_reg_mcion_pack(char *payload, u8 module) +{ + MLXSW_REG_ZERO(mcion, payload); + mlxsw_reg_mcion_module_set(payload, module); +} + /* MTPPS - Management Pulse Per Second Register * -------------------------------------------- * This register provides the device PPS capabilities, configure the PPS in and @@ -12200,9 +12439,11 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = { MLXSW_REG(pspa), MLXSW_REG(pmaos), MLXSW_REG(pplr), + MLXSW_REG(pmtdb), MLXSW_REG(pmpe), MLXSW_REG(pddr), - MLXSW_REG(pmtm), + MLXSW_REG(pmmp), + MLXSW_REG(pllp), MLXSW_REG(htgt), MLXSW_REG(hpkt), MLXSW_REG(rgcr), @@ -12210,6 +12451,7 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = { MLXSW_REG(rtar), MLXSW_REG(ratr), MLXSW_REG(rtdp), + MLXSW_REG(rips), MLXSW_REG(ratrad), MLXSW_REG(rdpm), MLXSW_REG(ricnt), @@ -12249,6 +12491,7 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = { MLXSW_REG(mgir), MLXSW_REG(mrsr), MLXSW_REG(mlcr), + MLXSW_REG(mcion), MLXSW_REG(mtpps), MLXSW_REG(mtutc), MLXSW_REG(mpsc), diff --git a/drivers/net/ethernet/mellanox/mlxsw/resources.h b/drivers/net/ethernet/mellanox/mlxsw/resources.h index a56c9e19a390..c7fc650608eb 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/resources.h +++ b/drivers/net/ethernet/mellanox/mlxsw/resources.h @@ -25,9 +25,6 @@ enum mlxsw_res_id { MLXSW_RES_ID_MAX_SYSTEM_PORT, MLXSW_RES_ID_MAX_LAG, MLXSW_RES_ID_MAX_LAG_MEMBERS, - MLXSW_RES_ID_LOCAL_PORTS_IN_1X, - MLXSW_RES_ID_LOCAL_PORTS_IN_2X, - MLXSW_RES_ID_LOCAL_PORTS_IN_4X, MLXSW_RES_ID_GUARANTEED_SHARED_BUFFER, MLXSW_RES_ID_CELL_SIZE, MLXSW_RES_ID_MAX_HEADROOM_SIZE, @@ -52,6 +49,7 @@ enum mlxsw_res_id { MLXSW_RES_ID_MAX_VRS, MLXSW_RES_ID_MAX_RIFS, MLXSW_RES_ID_MC_ERIF_LIST_ENTRIES, + MLXSW_RES_ID_MAX_RIF_MAC_PROFILES, MLXSW_RES_ID_MAX_LPM_TREES, MLXSW_RES_ID_MAX_NVE_MC_ENTRIES_IPV4, MLXSW_RES_ID_MAX_NVE_MC_ENTRIES_IPV6, @@ -84,9 +82,6 @@ static u16 mlxsw_res_ids[] = { [MLXSW_RES_ID_MAX_SYSTEM_PORT] = 0x2502, [MLXSW_RES_ID_MAX_LAG] = 0x2520, [MLXSW_RES_ID_MAX_LAG_MEMBERS] = 0x2521, - [MLXSW_RES_ID_LOCAL_PORTS_IN_1X] = 0x2610, - [MLXSW_RES_ID_LOCAL_PORTS_IN_2X] = 0x2611, - [MLXSW_RES_ID_LOCAL_PORTS_IN_4X] = 0x2612, [MLXSW_RES_ID_GUARANTEED_SHARED_BUFFER] = 0x2805, /* Bytes */ [MLXSW_RES_ID_CELL_SIZE] = 0x2803, /* Bytes */ [MLXSW_RES_ID_MAX_HEADROOM_SIZE] = 0x2811, /* Bytes */ @@ -111,6 +106,7 @@ static u16 mlxsw_res_ids[] = { [MLXSW_RES_ID_MAX_VRS] = 0x2C01, [MLXSW_RES_ID_MAX_RIFS] = 0x2C02, [MLXSW_RES_ID_MC_ERIF_LIST_ENTRIES] = 0x2C10, + [MLXSW_RES_ID_MAX_RIF_MAC_PROFILES] = 0x2C14, [MLXSW_RES_ID_MAX_LPM_TREES] = 0x2C30, [MLXSW_RES_ID_MAX_NVE_MC_ENTRIES_IPV4] = 0x2E02, [MLXSW_RES_ID_MAX_NVE_MC_ENTRIES_IPV6] = 0x2E03, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 250c5a24264d..5925db386b1b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -47,7 +47,7 @@ #define MLXSW_SP1_FWREV_MAJOR 13 #define MLXSW_SP1_FWREV_MINOR 2008 -#define MLXSW_SP1_FWREV_SUBMINOR 2406 +#define MLXSW_SP1_FWREV_SUBMINOR 3326 #define MLXSW_SP1_FWREV_CAN_RESET_MINOR 1702 static const struct mlxsw_fw_rev mlxsw_sp1_fw_rev = { @@ -64,7 +64,7 @@ static const struct mlxsw_fw_rev mlxsw_sp1_fw_rev = { #define MLXSW_SP2_FWREV_MAJOR 29 #define MLXSW_SP2_FWREV_MINOR 2008 -#define MLXSW_SP2_FWREV_SUBMINOR 2406 +#define MLXSW_SP2_FWREV_SUBMINOR 3326 static const struct mlxsw_fw_rev mlxsw_sp2_fw_rev = { .major = MLXSW_SP2_FWREV_MAJOR, @@ -79,7 +79,7 @@ static const struct mlxsw_fw_rev mlxsw_sp2_fw_rev = { #define MLXSW_SP3_FWREV_MAJOR 30 #define MLXSW_SP3_FWREV_MINOR 2008 -#define MLXSW_SP3_FWREV_SUBMINOR 2406 +#define MLXSW_SP3_FWREV_SUBMINOR 3326 static const struct mlxsw_fw_rev mlxsw_sp3_fw_rev = { .major = MLXSW_SP3_FWREV_MAJOR, @@ -316,11 +316,11 @@ static int mlxsw_sp_port_dev_addr_set(struct mlxsw_sp_port *mlxsw_sp_port, static int mlxsw_sp_port_dev_addr_init(struct mlxsw_sp_port *mlxsw_sp_port) { struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; - unsigned char *addr = mlxsw_sp_port->dev->dev_addr; - ether_addr_copy(addr, mlxsw_sp->base_mac); - addr[ETH_ALEN - 1] += mlxsw_sp_port->local_port; - return mlxsw_sp_port_dev_addr_set(mlxsw_sp_port, addr); + eth_hw_addr_gen(mlxsw_sp_port->dev, mlxsw_sp->base_mac, + mlxsw_sp_port->local_port); + return mlxsw_sp_port_dev_addr_set(mlxsw_sp_port, + mlxsw_sp_port->dev->dev_addr); } static int mlxsw_sp_port_max_mtu_get(struct mlxsw_sp_port *mlxsw_sp_port, int *p_max_mtu) @@ -351,12 +351,12 @@ static int mlxsw_sp_port_mtu_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 mtu) return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pmtu), pmtu_pl); } -static int mlxsw_sp_port_swid_set(struct mlxsw_sp_port *mlxsw_sp_port, u8 swid) +static int mlxsw_sp_port_swid_set(struct mlxsw_sp *mlxsw_sp, + u8 local_port, u8 swid) { - struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; char pspa_pl[MLXSW_REG_PSPA_LEN]; - mlxsw_reg_pspa_pack(pspa_pl, swid, mlxsw_sp_port->local_port); + mlxsw_reg_pspa_pack(pspa_pl, swid, local_port); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pspa), pspa_pl); } @@ -529,55 +529,80 @@ mlxsw_sp_port_module_info_get(struct mlxsw_sp *mlxsw_sp, u8 local_port, port_mapping->module = module; port_mapping->width = width; + port_mapping->module_width = width; port_mapping->lane = mlxsw_reg_pmlp_tx_lane_get(pmlp_pl, 0); return 0; } -static int mlxsw_sp_port_module_map(struct mlxsw_sp_port *mlxsw_sp_port) +static int +mlxsw_sp_port_module_map(struct mlxsw_sp *mlxsw_sp, u8 local_port, + const struct mlxsw_sp_port_mapping *port_mapping) { - struct mlxsw_sp_port_mapping *port_mapping = &mlxsw_sp_port->mapping; - struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; char pmlp_pl[MLXSW_REG_PMLP_LEN]; - int i; + int i, err; + + mlxsw_env_module_port_map(mlxsw_sp->core, port_mapping->module); - mlxsw_reg_pmlp_pack(pmlp_pl, mlxsw_sp_port->local_port); + mlxsw_reg_pmlp_pack(pmlp_pl, local_port); mlxsw_reg_pmlp_width_set(pmlp_pl, port_mapping->width); for (i = 0; i < port_mapping->width; i++) { mlxsw_reg_pmlp_module_set(pmlp_pl, i, port_mapping->module); mlxsw_reg_pmlp_tx_lane_set(pmlp_pl, i, port_mapping->lane + i); /* Rx & Tx */ } - return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pmlp), pmlp_pl); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pmlp), pmlp_pl); + if (err) + goto err_pmlp_write; + return 0; + +err_pmlp_write: + mlxsw_env_module_port_unmap(mlxsw_sp->core, port_mapping->module); + return err; } -static int mlxsw_sp_port_module_unmap(struct mlxsw_sp_port *mlxsw_sp_port) +static void mlxsw_sp_port_module_unmap(struct mlxsw_sp *mlxsw_sp, u8 local_port, + u8 module) { - struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; char pmlp_pl[MLXSW_REG_PMLP_LEN]; - mlxsw_reg_pmlp_pack(pmlp_pl, mlxsw_sp_port->local_port); + mlxsw_reg_pmlp_pack(pmlp_pl, local_port); mlxsw_reg_pmlp_width_set(pmlp_pl, 0); - return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pmlp), pmlp_pl); + mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pmlp), pmlp_pl); + mlxsw_env_module_port_unmap(mlxsw_sp->core, module); } static int mlxsw_sp_port_open(struct net_device *dev) { struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; int err; - err = mlxsw_sp_port_admin_status_set(mlxsw_sp_port, true); + err = mlxsw_env_module_port_up(mlxsw_sp->core, + mlxsw_sp_port->mapping.module); if (err) return err; + err = mlxsw_sp_port_admin_status_set(mlxsw_sp_port, true); + if (err) + goto err_port_admin_status_set; netif_start_queue(dev); return 0; + +err_port_admin_status_set: + mlxsw_env_module_port_down(mlxsw_sp->core, + mlxsw_sp_port->mapping.module); + return err; } static int mlxsw_sp_port_stop(struct net_device *dev) { struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; netif_stop_queue(dev); - return mlxsw_sp_port_admin_status_set(mlxsw_sp_port, false); + mlxsw_sp_port_admin_status_set(mlxsw_sp_port, false); + mlxsw_env_module_port_down(mlxsw_sp->core, + mlxsw_sp_port->mapping.module); + return 0; } static netdev_tx_t mlxsw_sp_port_xmit(struct sk_buff *skb, @@ -649,7 +674,7 @@ static int mlxsw_sp_port_set_mac_address(struct net_device *dev, void *p) err = mlxsw_sp_port_dev_addr_set(mlxsw_sp_port, addr->sa_data); if (err) return err; - memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + eth_hw_addr_set(dev, addr->sa_data); return 0; } @@ -799,12 +824,16 @@ mlxsw_sp_port_get_hw_xstats(struct net_device *dev, for (i = 0; i < TC_MAX_QUEUE; i++) { err = mlxsw_sp_port_get_stats_raw(dev, - MLXSW_REG_PPCNT_TC_CONG_TC, + MLXSW_REG_PPCNT_TC_CONG_CNT, i, ppcnt_pl); - if (!err) - xstats->wred_drop[i] = - mlxsw_reg_ppcnt_wred_discard_get(ppcnt_pl); + if (err) + goto tc_cnt; + xstats->wred_drop[i] = + mlxsw_reg_ppcnt_wred_discard_get(ppcnt_pl); + xstats->tc_ecn[i] = mlxsw_reg_ppcnt_ecn_marked_tc_get(ppcnt_pl); + +tc_cnt: err = mlxsw_sp_port_get_stats_raw(dev, MLXSW_REG_PPCNT_TC_CNT, i, ppcnt_pl); if (err) @@ -1010,6 +1039,8 @@ static int mlxsw_sp_setup_tc_block(struct mlxsw_sp_port *mlxsw_sp_port, return mlxsw_sp_setup_tc_block_clsact(mlxsw_sp_port, f, false); case FLOW_BLOCK_BINDER_TYPE_RED_EARLY_DROP: return mlxsw_sp_setup_tc_block_qevent_early_drop(mlxsw_sp_port, f); + case FLOW_BLOCK_BINDER_TYPE_RED_MARK: + return mlxsw_sp_setup_tc_block_qevent_mark(mlxsw_sp_port, f); default: return -EOPNOTSUPP; } @@ -1442,29 +1473,68 @@ mlxsw_sp_port_vlan_classification_set(struct mlxsw_sp_port *mlxsw_sp_port, return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spvc), spvc_pl); } +static int mlxsw_sp_port_label_info_get(struct mlxsw_sp *mlxsw_sp, + u8 local_port, u8 *port_number, + u8 *split_port_subnumber, + u8 *slot_index) +{ + char pllp_pl[MLXSW_REG_PLLP_LEN]; + int err; + + mlxsw_reg_pllp_pack(pllp_pl, local_port); + err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(pllp), pllp_pl); + if (err) + return err; + mlxsw_reg_pllp_unpack(pllp_pl, port_number, + split_port_subnumber, slot_index); + return 0; +} + static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port, - u8 split_base_local_port, + bool split, struct mlxsw_sp_port_mapping *port_mapping) { struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; - bool split = !!split_base_local_port; struct mlxsw_sp_port *mlxsw_sp_port; u32 lanes = port_mapping->width; + u8 split_port_subnumber; struct net_device *dev; + u8 port_number; + u8 slot_index; bool splittable; int err; + err = mlxsw_sp_port_module_map(mlxsw_sp, local_port, port_mapping); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to map module\n", + local_port); + return err; + } + + err = mlxsw_sp_port_swid_set(mlxsw_sp, local_port, 0); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to set SWID\n", + local_port); + goto err_port_swid_set; + } + + err = mlxsw_sp_port_label_info_get(mlxsw_sp, local_port, &port_number, + &split_port_subnumber, &slot_index); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to get port label information\n", + local_port); + goto err_port_label_info_get; + } + splittable = lanes > 1 && !split; err = mlxsw_core_port_init(mlxsw_sp->core, local_port, - port_mapping->module + 1, split, - port_mapping->lane / lanes, - splittable, lanes, - mlxsw_sp->base_mac, + port_number, split, split_port_subnumber, + splittable, lanes, mlxsw_sp->base_mac, sizeof(mlxsw_sp->base_mac)); if (err) { dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to init core port\n", local_port); - return err; + goto err_core_port_init; } dev = alloc_etherdev(sizeof(struct mlxsw_sp_port)); @@ -1480,7 +1550,6 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port, mlxsw_sp_port->local_port = local_port; mlxsw_sp_port->pvid = MLXSW_SP_DEFAULT_VID; mlxsw_sp_port->split = split; - mlxsw_sp_port->split_base_local_port = split_base_local_port; mlxsw_sp_port->mapping = *port_mapping; mlxsw_sp_port->link.autoneg = 1; INIT_LIST_HEAD(&mlxsw_sp_port->vlans_list); @@ -1498,20 +1567,6 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port, dev->netdev_ops = &mlxsw_sp_port_netdev_ops; dev->ethtool_ops = &mlxsw_sp_port_ethtool_ops; - err = mlxsw_sp_port_module_map(mlxsw_sp_port); - if (err) { - dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to map module\n", - mlxsw_sp_port->local_port); - goto err_port_module_map; - } - - err = mlxsw_sp_port_swid_set(mlxsw_sp_port, 0); - if (err) { - dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to set SWID\n", - mlxsw_sp_port->local_port); - goto err_port_swid_set; - } - err = mlxsw_sp_port_dev_addr_init(mlxsw_sp_port); if (err) { dev_err(mlxsw_sp->bus_info->dev, "Port %d: Unable to init port mac address\n", @@ -1712,21 +1767,24 @@ err_max_speed_get: err_port_speed_by_width_set: err_port_system_port_mapping_set: err_dev_addr_init: - mlxsw_sp_port_swid_set(mlxsw_sp_port, MLXSW_PORT_SWID_DISABLED_PORT); -err_port_swid_set: - mlxsw_sp_port_module_unmap(mlxsw_sp_port); -err_port_module_map: free_percpu(mlxsw_sp_port->pcpu_stats); err_alloc_stats: free_netdev(dev); err_alloc_etherdev: mlxsw_core_port_fini(mlxsw_sp->core, local_port); +err_core_port_init: +err_port_label_info_get: + mlxsw_sp_port_swid_set(mlxsw_sp, local_port, + MLXSW_PORT_SWID_DISABLED_PORT); +err_port_swid_set: + mlxsw_sp_port_module_unmap(mlxsw_sp, local_port, port_mapping->module); return err; } static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port) { struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp->ports[local_port]; + u8 module = mlxsw_sp_port->mapping.module; cancel_delayed_work_sync(&mlxsw_sp_port->periodic_hw_stats.update_dw); cancel_delayed_work_sync(&mlxsw_sp_port->ptp.shaper_dw); @@ -1742,12 +1800,13 @@ static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port) mlxsw_sp_port_dcb_fini(mlxsw_sp_port); mlxsw_sp_port_tc_mc_mode_set(mlxsw_sp_port, false); mlxsw_sp_port_buffers_fini(mlxsw_sp_port); - mlxsw_sp_port_swid_set(mlxsw_sp_port, MLXSW_PORT_SWID_DISABLED_PORT); - mlxsw_sp_port_module_unmap(mlxsw_sp_port); free_percpu(mlxsw_sp_port->pcpu_stats); WARN_ON_ONCE(!list_empty(&mlxsw_sp_port->vlans_list)); free_netdev(mlxsw_sp_port->dev); mlxsw_core_port_fini(mlxsw_sp->core, local_port); + mlxsw_sp_port_swid_set(mlxsw_sp, local_port, + MLXSW_PORT_SWID_DISABLED_PORT); + mlxsw_sp_port_module_unmap(mlxsw_sp, local_port, module); } static int mlxsw_sp_cpu_port_create(struct mlxsw_sp *mlxsw_sp) @@ -1789,8 +1848,15 @@ static void mlxsw_sp_cpu_port_remove(struct mlxsw_sp *mlxsw_sp) kfree(mlxsw_sp_port); } +static bool mlxsw_sp_local_port_valid(u8 local_port) +{ + return local_port != MLXSW_PORT_CPU_PORT; +} + static bool mlxsw_sp_port_created(struct mlxsw_sp *mlxsw_sp, u8 local_port) { + if (!mlxsw_sp_local_port_valid(local_port)) + return false; return mlxsw_sp->ports[local_port] != NULL; } @@ -1827,7 +1893,7 @@ static int mlxsw_sp_ports_create(struct mlxsw_sp *mlxsw_sp) port_mapping = mlxsw_sp->port_mapping[i]; if (!port_mapping) continue; - err = mlxsw_sp_port_create(mlxsw_sp, i, 0, port_mapping); + err = mlxsw_sp_port_create(mlxsw_sp, i, false, port_mapping); if (err) goto err_port_create; } @@ -1894,17 +1960,10 @@ static void mlxsw_sp_port_module_info_fini(struct mlxsw_sp *mlxsw_sp) kfree(mlxsw_sp->port_mapping); } -static u8 mlxsw_sp_cluster_base_port_get(u8 local_port, unsigned int max_width) -{ - u8 offset = (local_port - 1) % max_width; - - return local_port - offset; -} - static int -mlxsw_sp_port_split_create(struct mlxsw_sp *mlxsw_sp, u8 base_port, +mlxsw_sp_port_split_create(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_port_mapping *port_mapping, - unsigned int count, u8 offset) + unsigned int count, const char *pmtdb_pl) { struct mlxsw_sp_port_mapping split_port_mapping; int err, i; @@ -1912,8 +1971,13 @@ mlxsw_sp_port_split_create(struct mlxsw_sp *mlxsw_sp, u8 base_port, split_port_mapping = *port_mapping; split_port_mapping.width /= count; for (i = 0; i < count; i++) { - err = mlxsw_sp_port_create(mlxsw_sp, base_port + i * offset, - base_port, &split_port_mapping); + u8 s_local_port = mlxsw_reg_pmtdb_port_num_get(pmtdb_pl, i); + + if (!mlxsw_sp_local_port_valid(s_local_port)) + continue; + + err = mlxsw_sp_port_create(mlxsw_sp, s_local_port, + true, &split_port_mapping); if (err) goto err_port_create; split_port_mapping.lane += split_port_mapping.width; @@ -1922,49 +1986,34 @@ mlxsw_sp_port_split_create(struct mlxsw_sp *mlxsw_sp, u8 base_port, return 0; err_port_create: - for (i--; i >= 0; i--) - if (mlxsw_sp_port_created(mlxsw_sp, base_port + i * offset)) - mlxsw_sp_port_remove(mlxsw_sp, base_port + i * offset); + for (i--; i >= 0; i--) { + u8 s_local_port = mlxsw_reg_pmtdb_port_num_get(pmtdb_pl, i); + + if (mlxsw_sp_port_created(mlxsw_sp, s_local_port)) + mlxsw_sp_port_remove(mlxsw_sp, s_local_port); + } return err; } static void mlxsw_sp_port_unsplit_create(struct mlxsw_sp *mlxsw_sp, - u8 base_port, - unsigned int count, u8 offset) + unsigned int count, + const char *pmtdb_pl) { struct mlxsw_sp_port_mapping *port_mapping; int i; /* Go over original unsplit ports in the gap and recreate them. */ - for (i = 0; i < count * offset; i++) { - port_mapping = mlxsw_sp->port_mapping[base_port + i]; - if (!port_mapping) + for (i = 0; i < count; i++) { + u8 local_port = mlxsw_reg_pmtdb_port_num_get(pmtdb_pl, i); + + port_mapping = mlxsw_sp->port_mapping[local_port]; + if (!port_mapping || !mlxsw_sp_local_port_valid(local_port)) continue; - mlxsw_sp_port_create(mlxsw_sp, base_port + i, 0, port_mapping); + mlxsw_sp_port_create(mlxsw_sp, local_port, + false, port_mapping); } } -static int mlxsw_sp_local_ports_offset(struct mlxsw_core *mlxsw_core, - unsigned int count, - unsigned int max_width) -{ - enum mlxsw_res_id local_ports_in_x_res_id; - int split_width = max_width / count; - - if (split_width == 1) - local_ports_in_x_res_id = MLXSW_RES_ID_LOCAL_PORTS_IN_1X; - else if (split_width == 2) - local_ports_in_x_res_id = MLXSW_RES_ID_LOCAL_PORTS_IN_2X; - else if (split_width == 4) - local_ports_in_x_res_id = MLXSW_RES_ID_LOCAL_PORTS_IN_4X; - else - return -EINVAL; - - if (!mlxsw_core_res_valid(mlxsw_core, local_ports_in_x_res_id)) - return -EINVAL; - return mlxsw_core_res_get(mlxsw_core, local_ports_in_x_res_id); -} - static struct mlxsw_sp_port * mlxsw_sp_port_get_by_local_port(struct mlxsw_sp *mlxsw_sp, u8 local_port) { @@ -1980,9 +2029,8 @@ static int mlxsw_sp_port_split(struct mlxsw_core *mlxsw_core, u8 local_port, struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core); struct mlxsw_sp_port_mapping port_mapping; struct mlxsw_sp_port *mlxsw_sp_port; - int max_width; - u8 base_port; - int offset; + enum mlxsw_reg_pmtdb_status status; + char pmtdb_pl[MLXSW_REG_PMTDB_LEN]; int i; int err; @@ -1994,57 +2042,37 @@ static int mlxsw_sp_port_split(struct mlxsw_core *mlxsw_core, u8 local_port, return -EINVAL; } - max_width = mlxsw_core_module_max_width(mlxsw_core, - mlxsw_sp_port->mapping.module); - if (max_width < 0) { - netdev_err(mlxsw_sp_port->dev, "Cannot get max width of port module\n"); - NL_SET_ERR_MSG_MOD(extack, "Cannot get max width of port module"); - return max_width; + if (mlxsw_sp_port->split) { + NL_SET_ERR_MSG_MOD(extack, "Port is already split"); + return -EINVAL; } - /* Split port with non-max cannot be split. */ - if (mlxsw_sp_port->mapping.width != max_width) { - netdev_err(mlxsw_sp_port->dev, "Port cannot be split\n"); - NL_SET_ERR_MSG_MOD(extack, "Port cannot be split"); - return -EINVAL; + mlxsw_reg_pmtdb_pack(pmtdb_pl, 0, mlxsw_sp_port->mapping.module, + mlxsw_sp_port->mapping.module_width / count, + count); + err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(pmtdb), pmtdb_pl); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Failed to query split info"); + return err; } - offset = mlxsw_sp_local_ports_offset(mlxsw_core, count, max_width); - if (offset < 0) { - netdev_err(mlxsw_sp_port->dev, "Cannot obtain local port offset\n"); - NL_SET_ERR_MSG_MOD(extack, "Cannot obtain local port offset"); + status = mlxsw_reg_pmtdb_status_get(pmtdb_pl); + if (status != MLXSW_REG_PMTDB_STATUS_SUCCESS) { + NL_SET_ERR_MSG_MOD(extack, "Unsupported split configuration"); return -EINVAL; } - /* Only in case max split is being done, the local port and - * base port may differ. - */ - base_port = count == max_width ? - mlxsw_sp_cluster_base_port_get(local_port, max_width) : - local_port; + port_mapping = mlxsw_sp_port->mapping; - for (i = 0; i < count * offset; i++) { - /* Expect base port to exist and also the one in the middle in - * case of maximal split count. - */ - if (i == 0 || (count == max_width && i == count / 2)) - continue; + for (i = 0; i < count; i++) { + u8 s_local_port = mlxsw_reg_pmtdb_port_num_get(pmtdb_pl, i); - if (mlxsw_sp_port_created(mlxsw_sp, base_port + i)) { - netdev_err(mlxsw_sp_port->dev, "Invalid split configuration\n"); - NL_SET_ERR_MSG_MOD(extack, "Invalid split configuration"); - return -EINVAL; - } + if (mlxsw_sp_port_created(mlxsw_sp, s_local_port)) + mlxsw_sp_port_remove(mlxsw_sp, s_local_port); } - port_mapping = mlxsw_sp_port->mapping; - - for (i = 0; i < count; i++) - if (mlxsw_sp_port_created(mlxsw_sp, base_port + i * offset)) - mlxsw_sp_port_remove(mlxsw_sp, base_port + i * offset); - - err = mlxsw_sp_port_split_create(mlxsw_sp, base_port, &port_mapping, - count, offset); + err = mlxsw_sp_port_split_create(mlxsw_sp, &port_mapping, + count, pmtdb_pl); if (err) { dev_err(mlxsw_sp->bus_info->dev, "Failed to create split ports\n"); goto err_port_split_create; @@ -2053,7 +2081,7 @@ static int mlxsw_sp_port_split(struct mlxsw_core *mlxsw_core, u8 local_port, return 0; err_port_split_create: - mlxsw_sp_port_unsplit_create(mlxsw_sp, base_port, count, offset); + mlxsw_sp_port_unsplit_create(mlxsw_sp, count, pmtdb_pl); return err; } @@ -2062,11 +2090,10 @@ static int mlxsw_sp_port_unsplit(struct mlxsw_core *mlxsw_core, u8 local_port, { struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core); struct mlxsw_sp_port *mlxsw_sp_port; + char pmtdb_pl[MLXSW_REG_PMTDB_LEN]; unsigned int count; - int max_width; - u8 base_port; - int offset; int i; + int err; mlxsw_sp_port = mlxsw_sp_port_get_by_local_port(mlxsw_sp, local_port); if (!mlxsw_sp_port) { @@ -2077,35 +2104,30 @@ static int mlxsw_sp_port_unsplit(struct mlxsw_core *mlxsw_core, u8 local_port, } if (!mlxsw_sp_port->split) { - netdev_err(mlxsw_sp_port->dev, "Port was not split\n"); NL_SET_ERR_MSG_MOD(extack, "Port was not split"); return -EINVAL; } - max_width = mlxsw_core_module_max_width(mlxsw_core, - mlxsw_sp_port->mapping.module); - if (max_width < 0) { - netdev_err(mlxsw_sp_port->dev, "Cannot get max width of port module\n"); - NL_SET_ERR_MSG_MOD(extack, "Cannot get max width of port module"); - return max_width; - } - - count = max_width / mlxsw_sp_port->mapping.width; + count = mlxsw_sp_port->mapping.module_width / + mlxsw_sp_port->mapping.width; - offset = mlxsw_sp_local_ports_offset(mlxsw_core, count, max_width); - if (WARN_ON(offset < 0)) { - netdev_err(mlxsw_sp_port->dev, "Cannot obtain local port offset\n"); - NL_SET_ERR_MSG_MOD(extack, "Cannot obtain local port offset"); - return -EINVAL; + mlxsw_reg_pmtdb_pack(pmtdb_pl, 0, mlxsw_sp_port->mapping.module, + mlxsw_sp_port->mapping.module_width / count, + count); + err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(pmtdb), pmtdb_pl); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Failed to query split info"); + return err; } - base_port = mlxsw_sp_port->split_base_local_port; + for (i = 0; i < count; i++) { + u8 s_local_port = mlxsw_reg_pmtdb_port_num_get(pmtdb_pl, i); - for (i = 0; i < count; i++) - if (mlxsw_sp_port_created(mlxsw_sp, base_port + i * offset)) - mlxsw_sp_port_remove(mlxsw_sp, base_port + i * offset); + if (mlxsw_sp_port_created(mlxsw_sp, s_local_port)) + mlxsw_sp_port_remove(mlxsw_sp, s_local_port); + } - mlxsw_sp_port_unsplit_create(mlxsw_sp, base_port, count, offset); + mlxsw_sp_port_unsplit_create(mlxsw_sp, count, pmtdb_pl); return 0; } @@ -3260,6 +3282,30 @@ static int mlxsw_sp_resources_span_register(struct mlxsw_core *mlxsw_core) &span_size_params); } +static int +mlxsw_sp_resources_rif_mac_profile_register(struct mlxsw_core *mlxsw_core) +{ + struct devlink *devlink = priv_to_devlink(mlxsw_core); + struct devlink_resource_size_params size_params; + u8 max_rif_mac_profiles; + + if (!MLXSW_CORE_RES_VALID(mlxsw_core, MAX_RIF_MAC_PROFILES)) + return -EIO; + + max_rif_mac_profiles = MLXSW_CORE_RES_GET(mlxsw_core, + MAX_RIF_MAC_PROFILES); + devlink_resource_size_params_init(&size_params, max_rif_mac_profiles, + max_rif_mac_profiles, 1, + DEVLINK_RESOURCE_UNIT_ENTRY); + + return devlink_resource_register(devlink, + "rif_mac_profiles", + max_rif_mac_profiles, + MLXSW_SP_RESOURCE_RIF_MAC_PROFILES, + DEVLINK_RESOURCE_ID_PARENT_TOP, + &size_params); +} + static int mlxsw_sp1_resources_register(struct mlxsw_core *mlxsw_core) { int err; @@ -3278,10 +3324,16 @@ static int mlxsw_sp1_resources_register(struct mlxsw_core *mlxsw_core) err = mlxsw_sp_policer_resources_register(mlxsw_core); if (err) - goto err_resources_counter_register; + goto err_policer_resources_register; + + err = mlxsw_sp_resources_rif_mac_profile_register(mlxsw_core); + if (err) + goto err_resources_rif_mac_profile_register; return 0; +err_resources_rif_mac_profile_register: +err_policer_resources_register: err_resources_counter_register: err_resources_span_register: devlink_resources_unregister(priv_to_devlink(mlxsw_core), NULL); @@ -3306,10 +3358,16 @@ static int mlxsw_sp2_resources_register(struct mlxsw_core *mlxsw_core) err = mlxsw_sp_policer_resources_register(mlxsw_core); if (err) - goto err_resources_counter_register; + goto err_policer_resources_register; + + err = mlxsw_sp_resources_rif_mac_profile_register(mlxsw_core); + if (err) + goto err_resources_rif_mac_profile_register; return 0; +err_resources_rif_mac_profile_register: +err_policer_resources_register: err_resources_counter_register: err_resources_span_register: devlink_resources_unregister(priv_to_devlink(mlxsw_core), NULL); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 3a43cba6d23c..32fdd37657dd 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -67,6 +67,7 @@ enum mlxsw_sp_resource_id { MLXSW_SP_RESOURCE_COUNTERS_RIF, MLXSW_SP_RESOURCE_GLOBAL_POLICERS, MLXSW_SP_RESOURCE_SINGLE_RATE_POLICERS, + MLXSW_SP_RESOURCE_RIF_MAC_PROFILES, }; struct mlxsw_sp_port; @@ -144,7 +145,8 @@ struct mlxsw_sp_mall_entry; struct mlxsw_sp_port_mapping { u8 module; - u8 width; + u8 width; /* Number of lanes used by the port */ + u8 module_width; /* Number of lanes in the module (static) */ u8 lane; }; @@ -284,6 +286,7 @@ struct mlxsw_sp_port_vlan { /* No need an internal lock; At worse - miss a single periodic iteration */ struct mlxsw_sp_port_xstats { u64 ecn; + u64 tc_ecn[TC_MAX_QUEUE]; u64 wred_drop[TC_MAX_QUEUE]; u64 tail_drop[TC_MAX_QUEUE]; u64 backlog[TC_MAX_QUEUE]; @@ -345,7 +348,6 @@ struct mlxsw_sp_port { u16 egr_types; struct mlxsw_sp_ptp_port_stats stats; } ptp; - u8 split_base_local_port; int max_mtu; u32 max_speed; struct mlxsw_sp_hdroom *hdroom; @@ -747,6 +749,7 @@ enum mlxsw_sp_kvdl_entry_type { MLXSW_SP_KVDL_ENTRY_TYPE_ACTSET, MLXSW_SP_KVDL_ENTRY_TYPE_PBS, MLXSW_SP_KVDL_ENTRY_TYPE_MCRIGR, + MLXSW_SP_KVDL_ENTRY_TYPE_IPV6_ADDRESS, MLXSW_SP_KVDL_ENTRY_TYPE_TNUMT, }; @@ -758,6 +761,7 @@ mlxsw_sp_kvdl_entry_size(enum mlxsw_sp_kvdl_entry_type type) case MLXSW_SP_KVDL_ENTRY_TYPE_ACTSET: case MLXSW_SP_KVDL_ENTRY_TYPE_PBS: case MLXSW_SP_KVDL_ENTRY_TYPE_MCRIGR: + case MLXSW_SP_KVDL_ENTRY_TYPE_IPV6_ADDRESS: case MLXSW_SP_KVDL_ENTRY_TYPE_TNUMT: default: return 1; @@ -1193,6 +1197,8 @@ int mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port, struct tc_fifo_qopt_offload *p); int mlxsw_sp_setup_tc_block_qevent_early_drop(struct mlxsw_sp_port *mlxsw_sp_port, struct flow_block_offload *f); +int mlxsw_sp_setup_tc_block_qevent_mark(struct mlxsw_sp_port *mlxsw_sp_port, + struct flow_block_offload *f); /* spectrum_fid.c */ bool mlxsw_sp_fid_is_dummy(struct mlxsw_sp *mlxsw_sp, u16 fid_index); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum2_kvdl.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum2_kvdl.c index 3a73d654017f..10ae1115de6c 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum2_kvdl.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum2_kvdl.c @@ -35,6 +35,7 @@ static const struct mlxsw_sp2_kvdl_part_info mlxsw_sp2_kvdl_parts_info[] = { MAX_KVD_ACTION_SETS), MLXSW_SP2_KVDL_PART_INFO(PBS, 0x24, KVD_SIZE, KVD_SIZE), MLXSW_SP2_KVDL_PART_INFO(MCRIGR, 0x26, KVD_SIZE, KVD_SIZE), + MLXSW_SP2_KVDL_PART_INFO(IPV6_ADDRESS, 0x28, KVD_SIZE, KVD_SIZE), MLXSW_SP2_KVDL_PART_INFO(TNUMT, 0x29, KVD_SIZE, KVD_SIZE), }; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_atcam.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_atcam.c index ded4cf658680..4b713832fdd5 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_atcam.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_atcam.c @@ -119,7 +119,6 @@ mlxsw_sp_acl_atcam_region_12kb_init(struct mlxsw_sp_acl_atcam_region *aregion) { struct mlxsw_sp *mlxsw_sp = aregion->region->mlxsw_sp; struct mlxsw_sp_acl_atcam_region_12kb *region_12kb; - size_t alloc_size; u64 max_lkey_id; int err; @@ -131,8 +130,7 @@ mlxsw_sp_acl_atcam_region_12kb_init(struct mlxsw_sp_acl_atcam_region *aregion) if (!region_12kb) return -ENOMEM; - alloc_size = BITS_TO_LONGS(max_lkey_id) * sizeof(unsigned long); - region_12kb->used_lkey_id = kzalloc(alloc_size, GFP_KERNEL); + region_12kb->used_lkey_id = bitmap_zalloc(max_lkey_id, GFP_KERNEL); if (!region_12kb->used_lkey_id) { err = -ENOMEM; goto err_used_lkey_id_alloc; @@ -149,7 +147,7 @@ mlxsw_sp_acl_atcam_region_12kb_init(struct mlxsw_sp_acl_atcam_region *aregion) return 0; err_rhashtable_init: - kfree(region_12kb->used_lkey_id); + bitmap_free(region_12kb->used_lkey_id); err_used_lkey_id_alloc: kfree(region_12kb); return err; @@ -161,7 +159,7 @@ mlxsw_sp_acl_atcam_region_12kb_fini(struct mlxsw_sp_acl_atcam_region *aregion) struct mlxsw_sp_acl_atcam_region_12kb *region_12kb = aregion->priv; rhashtable_destroy(®ion_12kb->lkey_ht); - kfree(region_12kb->used_lkey_id); + bitmap_free(region_12kb->used_lkey_id); kfree(region_12kb); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c index 7cccc41dd69c..31f7f4c3acc3 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c @@ -36,7 +36,6 @@ int mlxsw_sp_acl_tcam_init(struct mlxsw_sp *mlxsw_sp, u64 max_tcam_regions; u64 max_regions; u64 max_groups; - size_t alloc_size; int err; mutex_init(&tcam->lock); @@ -52,15 +51,13 @@ int mlxsw_sp_acl_tcam_init(struct mlxsw_sp *mlxsw_sp, if (max_tcam_regions < max_regions) max_regions = max_tcam_regions; - alloc_size = sizeof(tcam->used_regions[0]) * BITS_TO_LONGS(max_regions); - tcam->used_regions = kzalloc(alloc_size, GFP_KERNEL); + tcam->used_regions = bitmap_zalloc(max_regions, GFP_KERNEL); if (!tcam->used_regions) return -ENOMEM; tcam->max_regions = max_regions; max_groups = MLXSW_CORE_RES_GET(mlxsw_sp->core, ACL_MAX_GROUPS); - alloc_size = sizeof(tcam->used_groups[0]) * BITS_TO_LONGS(max_groups); - tcam->used_groups = kzalloc(alloc_size, GFP_KERNEL); + tcam->used_groups = bitmap_zalloc(max_groups, GFP_KERNEL); if (!tcam->used_groups) { err = -ENOMEM; goto err_alloc_used_groups; @@ -76,9 +73,9 @@ int mlxsw_sp_acl_tcam_init(struct mlxsw_sp *mlxsw_sp, return 0; err_tcam_init: - kfree(tcam->used_groups); + bitmap_free(tcam->used_groups); err_alloc_used_groups: - kfree(tcam->used_regions); + bitmap_free(tcam->used_regions); return err; } @@ -89,8 +86,8 @@ void mlxsw_sp_acl_tcam_fini(struct mlxsw_sp *mlxsw_sp, mutex_destroy(&tcam->lock); ops->fini(mlxsw_sp, tcam->priv); - kfree(tcam->used_groups); - kfree(tcam->used_regions); + bitmap_free(tcam->used_groups); + bitmap_free(tcam->used_regions); } int mlxsw_sp_acl_tcam_priority_get(struct mlxsw_sp *mlxsw_sp, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c index 9de160e740b2..d78cf5a7220a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c @@ -1583,7 +1583,7 @@ int mlxsw_sp_sb_occ_snapshot(struct mlxsw_core *mlxsw_core, { struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core); struct mlxsw_sp_sb_sr_occ_query_cb_ctx cb_ctx; - unsigned long cb_priv; + unsigned long cb_priv = 0; LIST_HEAD(bulk_list); char *sbsr_pl; u8 masked_count; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c index b65b93a2b9bc..fc2257753b9b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c @@ -122,7 +122,6 @@ int mlxsw_sp_counter_pool_init(struct mlxsw_sp *mlxsw_sp) unsigned int sub_pools_count = ARRAY_SIZE(mlxsw_sp_counter_sub_pools); struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); struct mlxsw_sp_counter_pool *pool; - unsigned int map_size; int err; pool = kzalloc(struct_size(pool, sub_pools, sub_pools_count), @@ -143,9 +142,7 @@ int mlxsw_sp_counter_pool_init(struct mlxsw_sp *mlxsw_sp) devlink_resource_occ_get_register(devlink, MLXSW_SP_RESOURCE_COUNTERS, mlxsw_sp_counter_pool_occ_get, pool); - map_size = BITS_TO_LONGS(pool->pool_size) * sizeof(unsigned long); - - pool->usage = kzalloc(map_size, GFP_KERNEL); + pool->usage = bitmap_zalloc(pool->pool_size, GFP_KERNEL); if (!pool->usage) { err = -ENOMEM; goto err_usage_alloc; @@ -158,7 +155,7 @@ int mlxsw_sp_counter_pool_init(struct mlxsw_sp *mlxsw_sp) return 0; err_sub_pools_init: - kfree(pool->usage); + bitmap_free(pool->usage); err_usage_alloc: devlink_resource_occ_get_unregister(devlink, MLXSW_SP_RESOURCE_COUNTERS); @@ -176,7 +173,7 @@ void mlxsw_sp_counter_pool_fini(struct mlxsw_sp *mlxsw_sp) WARN_ON(find_first_bit(pool->usage, pool->pool_size) != pool->pool_size); WARN_ON(atomic_read(&pool->active_entries_count)); - kfree(pool->usage); + bitmap_free(pool->usage); devlink_resource_occ_get_unregister(devlink, MLXSW_SP_RESOURCE_COUNTERS); kfree(pool); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c index 267590a0eee7..84d4460f3dcd 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c @@ -96,6 +96,9 @@ mlxsw_sp_link_ext_state_opcode_map[] = { {1032, ETHTOOL_LINK_EXT_STATE_POWER_BUDGET_EXCEEDED, 0}, {1030, ETHTOOL_LINK_EXT_STATE_OVERHEAT, 0}, + + {1042, ETHTOOL_LINK_EXT_STATE_MODULE, + ETHTOOL_LINK_EXT_SUBSTATE_MODULE_CMIS_NOT_READY}, }; static void @@ -124,6 +127,10 @@ mlxsw_sp_port_set_link_ext_state(struct mlxsw_sp_ethtool_link_ext_state_opcode_m link_ext_state_info->cable_issue = link_ext_state_mapping.link_ext_substate; break; + case ETHTOOL_LINK_EXT_STATE_MODULE: + link_ext_state_info->module = + link_ext_state_mapping.link_ext_substate; + break; default: break; } @@ -1197,6 +1204,41 @@ mlxsw_sp_get_rmon_stats(struct net_device *dev, *ranges = mlxsw_rmon_ranges; } +static int mlxsw_sp_reset(struct net_device *dev, u32 *flags) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + u8 module = mlxsw_sp_port->mapping.module; + + return mlxsw_env_reset_module(dev, mlxsw_sp->core, module, flags); +} + +static int +mlxsw_sp_get_module_power_mode(struct net_device *dev, + struct ethtool_module_power_mode_params *params, + struct netlink_ext_ack *extack) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + u8 module = mlxsw_sp_port->mapping.module; + + return mlxsw_env_get_module_power_mode(mlxsw_sp->core, module, params, + extack); +} + +static int +mlxsw_sp_set_module_power_mode(struct net_device *dev, + const struct ethtool_module_power_mode_params *params, + struct netlink_ext_ack *extack) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + u8 module = mlxsw_sp_port->mapping.module; + + return mlxsw_env_set_module_power_mode(mlxsw_sp->core, module, + params->policy, extack); +} + const struct ethtool_ops mlxsw_sp_port_ethtool_ops = { .cap_link_lanes_supported = true, .get_drvinfo = mlxsw_sp_port_get_drvinfo, @@ -1218,6 +1260,9 @@ const struct ethtool_ops mlxsw_sp_port_ethtool_ops = { .get_eth_mac_stats = mlxsw_sp_get_eth_mac_stats, .get_eth_ctrl_stats = mlxsw_sp_get_eth_ctrl_stats, .get_rmon_stats = mlxsw_sp_get_rmon_stats, + .reset = mlxsw_sp_reset, + .get_module_power_mode = mlxsw_sp_get_module_power_mode, + .set_module_power_mode = mlxsw_sp_set_module_power_mode, }; struct mlxsw_sp1_port_link_mode { diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c index 5facabd86882..ad3926de88f2 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c @@ -24,50 +24,72 @@ mlxsw_sp_ipip_netdev_parms6(const struct net_device *ol_dev) return tun->parms; } -static bool mlxsw_sp_ipip_parms4_has_ikey(struct ip_tunnel_parm parms) +static bool mlxsw_sp_ipip_parms4_has_ikey(const struct ip_tunnel_parm *parms) { - return !!(parms.i_flags & TUNNEL_KEY); + return !!(parms->i_flags & TUNNEL_KEY); } -static bool mlxsw_sp_ipip_parms4_has_okey(struct ip_tunnel_parm parms) +static bool mlxsw_sp_ipip_parms6_has_ikey(const struct __ip6_tnl_parm *parms) { - return !!(parms.o_flags & TUNNEL_KEY); + return !!(parms->i_flags & TUNNEL_KEY); } -static u32 mlxsw_sp_ipip_parms4_ikey(struct ip_tunnel_parm parms) +static bool mlxsw_sp_ipip_parms4_has_okey(const struct ip_tunnel_parm *parms) +{ + return !!(parms->o_flags & TUNNEL_KEY); +} + +static bool mlxsw_sp_ipip_parms6_has_okey(const struct __ip6_tnl_parm *parms) +{ + return !!(parms->o_flags & TUNNEL_KEY); +} + +static u32 mlxsw_sp_ipip_parms4_ikey(const struct ip_tunnel_parm *parms) { return mlxsw_sp_ipip_parms4_has_ikey(parms) ? - be32_to_cpu(parms.i_key) : 0; + be32_to_cpu(parms->i_key) : 0; } -static u32 mlxsw_sp_ipip_parms4_okey(struct ip_tunnel_parm parms) +static u32 mlxsw_sp_ipip_parms6_ikey(const struct __ip6_tnl_parm *parms) +{ + return mlxsw_sp_ipip_parms6_has_ikey(parms) ? + be32_to_cpu(parms->i_key) : 0; +} + +static u32 mlxsw_sp_ipip_parms4_okey(const struct ip_tunnel_parm *parms) { return mlxsw_sp_ipip_parms4_has_okey(parms) ? - be32_to_cpu(parms.o_key) : 0; + be32_to_cpu(parms->o_key) : 0; +} + +static u32 mlxsw_sp_ipip_parms6_okey(const struct __ip6_tnl_parm *parms) +{ + return mlxsw_sp_ipip_parms6_has_okey(parms) ? + be32_to_cpu(parms->o_key) : 0; } static union mlxsw_sp_l3addr -mlxsw_sp_ipip_parms4_saddr(struct ip_tunnel_parm parms) +mlxsw_sp_ipip_parms4_saddr(const struct ip_tunnel_parm *parms) { - return (union mlxsw_sp_l3addr) { .addr4 = parms.iph.saddr }; + return (union mlxsw_sp_l3addr) { .addr4 = parms->iph.saddr }; } static union mlxsw_sp_l3addr -mlxsw_sp_ipip_parms6_saddr(struct __ip6_tnl_parm parms) +mlxsw_sp_ipip_parms6_saddr(const struct __ip6_tnl_parm *parms) { - return (union mlxsw_sp_l3addr) { .addr6 = parms.laddr }; + return (union mlxsw_sp_l3addr) { .addr6 = parms->laddr }; } static union mlxsw_sp_l3addr -mlxsw_sp_ipip_parms4_daddr(struct ip_tunnel_parm parms) +mlxsw_sp_ipip_parms4_daddr(const struct ip_tunnel_parm *parms) { - return (union mlxsw_sp_l3addr) { .addr4 = parms.iph.daddr }; + return (union mlxsw_sp_l3addr) { .addr4 = parms->iph.daddr }; } static union mlxsw_sp_l3addr -mlxsw_sp_ipip_parms6_daddr(struct __ip6_tnl_parm parms) +mlxsw_sp_ipip_parms6_daddr(const struct __ip6_tnl_parm *parms) { - return (union mlxsw_sp_l3addr) { .addr6 = parms.raddr }; + return (union mlxsw_sp_l3addr) { .addr6 = parms->raddr }; } union mlxsw_sp_l3addr @@ -80,10 +102,10 @@ mlxsw_sp_ipip_netdev_saddr(enum mlxsw_sp_l3proto proto, switch (proto) { case MLXSW_SP_L3_PROTO_IPV4: parms4 = mlxsw_sp_ipip_netdev_parms4(ol_dev); - return mlxsw_sp_ipip_parms4_saddr(parms4); + return mlxsw_sp_ipip_parms4_saddr(&parms4); case MLXSW_SP_L3_PROTO_IPV6: parms6 = mlxsw_sp_ipip_netdev_parms6(ol_dev); - return mlxsw_sp_ipip_parms6_saddr(parms6); + return mlxsw_sp_ipip_parms6_saddr(&parms6); } WARN_ON(1); @@ -95,7 +117,7 @@ static __be32 mlxsw_sp_ipip_netdev_daddr4(const struct net_device *ol_dev) struct ip_tunnel_parm parms4 = mlxsw_sp_ipip_netdev_parms4(ol_dev); - return mlxsw_sp_ipip_parms4_daddr(parms4).addr4; + return mlxsw_sp_ipip_parms4_daddr(&parms4).addr4; } static union mlxsw_sp_l3addr @@ -108,10 +130,10 @@ mlxsw_sp_ipip_netdev_daddr(enum mlxsw_sp_l3proto proto, switch (proto) { case MLXSW_SP_L3_PROTO_IPV4: parms4 = mlxsw_sp_ipip_netdev_parms4(ol_dev); - return mlxsw_sp_ipip_parms4_daddr(parms4); + return mlxsw_sp_ipip_parms4_daddr(&parms4); case MLXSW_SP_L3_PROTO_IPV6: parms6 = mlxsw_sp_ipip_netdev_parms6(ol_dev); - return mlxsw_sp_ipip_parms6_daddr(parms6); + return mlxsw_sp_ipip_parms6_daddr(&parms6); } WARN_ON(1); @@ -125,6 +147,21 @@ bool mlxsw_sp_l3addr_is_zero(union mlxsw_sp_l3addr addr) return !memcmp(&addr, &naddr, sizeof(naddr)); } +static struct mlxsw_sp_ipip_parms +mlxsw_sp_ipip_netdev_parms_init_gre4(const struct net_device *ol_dev) +{ + struct ip_tunnel_parm parms = mlxsw_sp_ipip_netdev_parms4(ol_dev); + + return (struct mlxsw_sp_ipip_parms) { + .proto = MLXSW_SP_L3_PROTO_IPV4, + .saddr = mlxsw_sp_ipip_parms4_saddr(&parms), + .daddr = mlxsw_sp_ipip_parms4_daddr(&parms), + .link = parms.link, + .ikey = mlxsw_sp_ipip_parms4_ikey(&parms), + .okey = mlxsw_sp_ipip_parms4_okey(&parms), + }; +} + static int mlxsw_sp_ipip_nexthop_update_gre4(struct mlxsw_sp *mlxsw_sp, u32 adj_index, struct mlxsw_sp_ipip_entry *ipip_entry, @@ -158,8 +195,8 @@ mlxsw_sp_ipip_decap_config_gre4(struct mlxsw_sp *mlxsw_sp, u32 ikey; parms = mlxsw_sp_ipip_netdev_parms4(ipip_entry->ol_dev); - has_ikey = mlxsw_sp_ipip_parms4_has_ikey(parms); - ikey = mlxsw_sp_ipip_parms4_ikey(parms); + has_ikey = mlxsw_sp_ipip_parms4_has_ikey(&parms); + ikey = mlxsw_sp_ipip_parms4_ikey(&parms); mlxsw_reg_rtdp_pack(rtdp_pl, MLXSW_REG_RTDP_TYPE_IPIP, tunnel_index); mlxsw_reg_rtdp_egress_router_interface_set(rtdp_pl, ul_rif_id); @@ -218,12 +255,12 @@ mlxsw_sp_ipip_ol_loopback_config_gre4(struct mlxsw_sp *mlxsw_sp, struct ip_tunnel_parm parms = mlxsw_sp_ipip_netdev_parms4(ol_dev); enum mlxsw_reg_ritr_loopback_ipip_type lb_ipipt; - lb_ipipt = mlxsw_sp_ipip_parms4_has_okey(parms) ? + lb_ipipt = mlxsw_sp_ipip_parms4_has_okey(&parms) ? MLXSW_REG_RITR_LOOPBACK_IPIP_TYPE_IP_IN_GRE_KEY_IN_IP : MLXSW_REG_RITR_LOOPBACK_IPIP_TYPE_IP_IN_GRE_IN_IP; return (struct mlxsw_sp_rif_ipip_lb_config){ .lb_ipipt = lb_ipipt, - .okey = mlxsw_sp_ipip_parms4_okey(parms), + .okey = mlxsw_sp_ipip_parms4_okey(&parms), .ul_protocol = MLXSW_SP_L3_PROTO_IPV4, .saddr = mlxsw_sp_ipip_netdev_saddr(MLXSW_SP_L3_PROTO_IPV4, ol_dev), @@ -231,48 +268,39 @@ mlxsw_sp_ipip_ol_loopback_config_gre4(struct mlxsw_sp *mlxsw_sp, } static int -mlxsw_sp_ipip_ol_netdev_change_gre4(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_ipip_entry *ipip_entry, - struct netlink_ext_ack *extack) +mlxsw_sp_ipip_ol_netdev_change_gre(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_ipip_entry *ipip_entry, + const struct mlxsw_sp_ipip_parms *new_parms, + struct netlink_ext_ack *extack) { - union mlxsw_sp_l3addr old_saddr, new_saddr; - union mlxsw_sp_l3addr old_daddr, new_daddr; - struct ip_tunnel_parm new_parms; + const struct mlxsw_sp_ipip_parms *old_parms = &ipip_entry->parms; bool update_tunnel = false; bool update_decap = false; bool update_nhs = false; int err = 0; - new_parms = mlxsw_sp_ipip_netdev_parms4(ipip_entry->ol_dev); - - new_saddr = mlxsw_sp_ipip_parms4_saddr(new_parms); - old_saddr = mlxsw_sp_ipip_parms4_saddr(ipip_entry->parms4); - new_daddr = mlxsw_sp_ipip_parms4_daddr(new_parms); - old_daddr = mlxsw_sp_ipip_parms4_daddr(ipip_entry->parms4); - - if (!mlxsw_sp_l3addr_eq(&new_saddr, &old_saddr)) { + if (!mlxsw_sp_l3addr_eq(&new_parms->saddr, &old_parms->saddr)) { u16 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ipip_entry->ol_dev); /* Since the local address has changed, if there is another * tunnel with a matching saddr, both need to be demoted. */ if (mlxsw_sp_ipip_demote_tunnel_by_saddr(mlxsw_sp, - MLXSW_SP_L3_PROTO_IPV4, - new_saddr, ul_tb_id, + new_parms->proto, + new_parms->saddr, + ul_tb_id, ipip_entry)) { mlxsw_sp_ipip_entry_demote_tunnel(mlxsw_sp, ipip_entry); return 0; } update_tunnel = true; - } else if ((mlxsw_sp_ipip_parms4_okey(ipip_entry->parms4) != - mlxsw_sp_ipip_parms4_okey(new_parms)) || - ipip_entry->parms4.link != new_parms.link) { + } else if (old_parms->okey != new_parms->okey || + old_parms->link != new_parms->link) { update_tunnel = true; - } else if (!mlxsw_sp_l3addr_eq(&new_daddr, &old_daddr)) { + } else if (!mlxsw_sp_l3addr_eq(&new_parms->daddr, &old_parms->daddr)) { update_nhs = true; - } else if (mlxsw_sp_ipip_parms4_ikey(ipip_entry->parms4) != - mlxsw_sp_ipip_parms4_ikey(new_parms)) { + } else if (old_parms->ikey != new_parms->ikey) { update_decap = true; } @@ -288,23 +316,308 @@ mlxsw_sp_ipip_ol_netdev_change_gre4(struct mlxsw_sp *mlxsw_sp, err = __mlxsw_sp_ipip_entry_update_tunnel(mlxsw_sp, ipip_entry, false, false, false, extack); + if (err) + return err; - ipip_entry->parms4 = new_parms; - return err; + ipip_entry->parms = *new_parms; + return 0; +} + +static int +mlxsw_sp_ipip_ol_netdev_change_gre4(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_ipip_entry *ipip_entry, + struct netlink_ext_ack *extack) +{ + struct mlxsw_sp_ipip_parms new_parms; + + new_parms = mlxsw_sp_ipip_netdev_parms_init_gre4(ipip_entry->ol_dev); + return mlxsw_sp_ipip_ol_netdev_change_gre(mlxsw_sp, ipip_entry, + &new_parms, extack); +} + +static int +mlxsw_sp_ipip_rem_addr_set_gre4(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_ipip_entry *ipip_entry) +{ + return 0; +} + +static void +mlxsw_sp_ipip_rem_addr_unset_gre4(struct mlxsw_sp *mlxsw_sp, + const struct mlxsw_sp_ipip_entry *ipip_entry) +{ } static const struct mlxsw_sp_ipip_ops mlxsw_sp_ipip_gre4_ops = { .dev_type = ARPHRD_IPGRE, .ul_proto = MLXSW_SP_L3_PROTO_IPV4, + .inc_parsing_depth = false, + .parms_init = mlxsw_sp_ipip_netdev_parms_init_gre4, .nexthop_update = mlxsw_sp_ipip_nexthop_update_gre4, .decap_config = mlxsw_sp_ipip_decap_config_gre4, .can_offload = mlxsw_sp_ipip_can_offload_gre4, .ol_loopback_config = mlxsw_sp_ipip_ol_loopback_config_gre4, .ol_netdev_change = mlxsw_sp_ipip_ol_netdev_change_gre4, + .rem_ip_addr_set = mlxsw_sp_ipip_rem_addr_set_gre4, + .rem_ip_addr_unset = mlxsw_sp_ipip_rem_addr_unset_gre4, }; -const struct mlxsw_sp_ipip_ops *mlxsw_sp_ipip_ops_arr[] = { +static struct mlxsw_sp_ipip_parms +mlxsw_sp1_ipip_netdev_parms_init_gre6(const struct net_device *ol_dev) +{ + struct mlxsw_sp_ipip_parms parms = {0}; + + WARN_ON_ONCE(1); + return parms; +} + +static int +mlxsw_sp1_ipip_nexthop_update_gre6(struct mlxsw_sp *mlxsw_sp, u32 adj_index, + struct mlxsw_sp_ipip_entry *ipip_entry, + bool force, char *ratr_pl) +{ + WARN_ON_ONCE(1); + return -EINVAL; +} + +static int +mlxsw_sp1_ipip_decap_config_gre6(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_ipip_entry *ipip_entry, + u32 tunnel_index) +{ + WARN_ON_ONCE(1); + return -EINVAL; +} + +static bool mlxsw_sp1_ipip_can_offload_gre6(const struct mlxsw_sp *mlxsw_sp, + const struct net_device *ol_dev) +{ + return false; +} + +static struct mlxsw_sp_rif_ipip_lb_config +mlxsw_sp1_ipip_ol_loopback_config_gre6(struct mlxsw_sp *mlxsw_sp, + const struct net_device *ol_dev) +{ + struct mlxsw_sp_rif_ipip_lb_config config = {0}; + + WARN_ON_ONCE(1); + return config; +} + +static int +mlxsw_sp1_ipip_ol_netdev_change_gre6(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_ipip_entry *ipip_entry, + struct netlink_ext_ack *extack) +{ + WARN_ON_ONCE(1); + return -EINVAL; +} + +static int +mlxsw_sp1_ipip_rem_addr_set_gre6(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_ipip_entry *ipip_entry) +{ + WARN_ON_ONCE(1); + return -EINVAL; +} + +static void +mlxsw_sp1_ipip_rem_addr_unset_gre6(struct mlxsw_sp *mlxsw_sp, + const struct mlxsw_sp_ipip_entry *ipip_entry) +{ + WARN_ON_ONCE(1); +} + +static const struct mlxsw_sp_ipip_ops mlxsw_sp1_ipip_gre6_ops = { + .dev_type = ARPHRD_IP6GRE, + .ul_proto = MLXSW_SP_L3_PROTO_IPV6, + .inc_parsing_depth = true, + .parms_init = mlxsw_sp1_ipip_netdev_parms_init_gre6, + .nexthop_update = mlxsw_sp1_ipip_nexthop_update_gre6, + .decap_config = mlxsw_sp1_ipip_decap_config_gre6, + .can_offload = mlxsw_sp1_ipip_can_offload_gre6, + .ol_loopback_config = mlxsw_sp1_ipip_ol_loopback_config_gre6, + .ol_netdev_change = mlxsw_sp1_ipip_ol_netdev_change_gre6, + .rem_ip_addr_set = mlxsw_sp1_ipip_rem_addr_set_gre6, + .rem_ip_addr_unset = mlxsw_sp1_ipip_rem_addr_unset_gre6, +}; + +const struct mlxsw_sp_ipip_ops *mlxsw_sp1_ipip_ops_arr[] = { [MLXSW_SP_IPIP_TYPE_GRE4] = &mlxsw_sp_ipip_gre4_ops, + [MLXSW_SP_IPIP_TYPE_GRE6] = &mlxsw_sp1_ipip_gre6_ops, +}; + +static struct mlxsw_sp_ipip_parms +mlxsw_sp2_ipip_netdev_parms_init_gre6(const struct net_device *ol_dev) +{ + struct __ip6_tnl_parm parms = mlxsw_sp_ipip_netdev_parms6(ol_dev); + + return (struct mlxsw_sp_ipip_parms) { + .proto = MLXSW_SP_L3_PROTO_IPV6, + .saddr = mlxsw_sp_ipip_parms6_saddr(&parms), + .daddr = mlxsw_sp_ipip_parms6_daddr(&parms), + .link = parms.link, + .ikey = mlxsw_sp_ipip_parms6_ikey(&parms), + .okey = mlxsw_sp_ipip_parms6_okey(&parms), + }; +} + +static int +mlxsw_sp2_ipip_nexthop_update_gre6(struct mlxsw_sp *mlxsw_sp, u32 adj_index, + struct mlxsw_sp_ipip_entry *ipip_entry, + bool force, char *ratr_pl) +{ + u16 rif_index = mlxsw_sp_ipip_lb_rif_index(ipip_entry->ol_lb); + enum mlxsw_reg_ratr_op op; + + op = force ? MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY : + MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY_ON_ACTIVITY; + mlxsw_reg_ratr_pack(ratr_pl, op, true, MLXSW_REG_RATR_TYPE_IPIP, + adj_index, rif_index); + mlxsw_reg_ratr_ipip6_entry_pack(ratr_pl, + ipip_entry->dip_kvdl_index); + + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl); +} + +static int +mlxsw_sp2_ipip_decap_config_gre6(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_ipip_entry *ipip_entry, + u32 tunnel_index) +{ + u16 rif_index = mlxsw_sp_ipip_lb_rif_index(ipip_entry->ol_lb); + u16 ul_rif_id = mlxsw_sp_ipip_lb_ul_rif_id(ipip_entry->ol_lb); + char rtdp_pl[MLXSW_REG_RTDP_LEN]; + struct __ip6_tnl_parm parms; + unsigned int type_check; + bool has_ikey; + u32 ikey; + + parms = mlxsw_sp_ipip_netdev_parms6(ipip_entry->ol_dev); + has_ikey = mlxsw_sp_ipip_parms6_has_ikey(&parms); + ikey = mlxsw_sp_ipip_parms6_ikey(&parms); + + mlxsw_reg_rtdp_pack(rtdp_pl, MLXSW_REG_RTDP_TYPE_IPIP, tunnel_index); + mlxsw_reg_rtdp_egress_router_interface_set(rtdp_pl, ul_rif_id); + + type_check = has_ikey ? + MLXSW_REG_RTDP_IPIP_TYPE_CHECK_ALLOW_GRE_KEY : + MLXSW_REG_RTDP_IPIP_TYPE_CHECK_ALLOW_GRE; + + /* Linux demuxes tunnels based on packet SIP (which must match tunnel + * remote IP). Thus configure decap so that it filters out packets that + * are not IPv6 or have the wrong SIP. IPIP_DECAP_ERROR trap is + * generated for packets that fail this criterion. Linux then handles + * such packets in slow path and generates ICMP destination unreachable. + */ + mlxsw_reg_rtdp_ipip6_pack(rtdp_pl, rif_index, + MLXSW_REG_RTDP_IPIP_SIP_CHECK_FILTER_IPV6, + type_check, has_ikey, + ipip_entry->dip_kvdl_index, ikey); + + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rtdp), rtdp_pl); +} + +static bool mlxsw_sp2_ipip_can_offload_gre6(const struct mlxsw_sp *mlxsw_sp, + const struct net_device *ol_dev) +{ + struct __ip6_tnl_parm tparm = mlxsw_sp_ipip_netdev_parms6(ol_dev); + bool inherit_tos = tparm.flags & IP6_TNL_F_USE_ORIG_TCLASS; + bool inherit_ttl = tparm.hop_limit == 0; + __be16 okflags = TUNNEL_KEY; /* We can't offload any other features. */ + + return (tparm.i_flags & ~okflags) == 0 && + (tparm.o_flags & ~okflags) == 0 && + inherit_ttl && inherit_tos && + mlxsw_sp_ipip_tunnel_complete(MLXSW_SP_L3_PROTO_IPV6, ol_dev); +} + +static struct mlxsw_sp_rif_ipip_lb_config +mlxsw_sp2_ipip_ol_loopback_config_gre6(struct mlxsw_sp *mlxsw_sp, + const struct net_device *ol_dev) +{ + struct __ip6_tnl_parm parms = mlxsw_sp_ipip_netdev_parms6(ol_dev); + enum mlxsw_reg_ritr_loopback_ipip_type lb_ipipt; + + lb_ipipt = mlxsw_sp_ipip_parms6_has_okey(&parms) ? + MLXSW_REG_RITR_LOOPBACK_IPIP_TYPE_IP_IN_GRE_KEY_IN_IP : + MLXSW_REG_RITR_LOOPBACK_IPIP_TYPE_IP_IN_GRE_IN_IP; + return (struct mlxsw_sp_rif_ipip_lb_config){ + .lb_ipipt = lb_ipipt, + .okey = mlxsw_sp_ipip_parms6_okey(&parms), + .ul_protocol = MLXSW_SP_L3_PROTO_IPV6, + .saddr = mlxsw_sp_ipip_netdev_saddr(MLXSW_SP_L3_PROTO_IPV6, + ol_dev), + }; +} + +static int +mlxsw_sp2_ipip_ol_netdev_change_gre6(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_ipip_entry *ipip_entry, + struct netlink_ext_ack *extack) +{ + struct mlxsw_sp_ipip_parms new_parms; + + new_parms = mlxsw_sp2_ipip_netdev_parms_init_gre6(ipip_entry->ol_dev); + return mlxsw_sp_ipip_ol_netdev_change_gre(mlxsw_sp, ipip_entry, + &new_parms, extack); +} + +static int +mlxsw_sp2_ipip_rem_addr_set_gre6(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_ipip_entry *ipip_entry) +{ + char rips_pl[MLXSW_REG_RIPS_LEN]; + struct __ip6_tnl_parm parms6; + int err; + + err = mlxsw_sp_kvdl_alloc(mlxsw_sp, + MLXSW_SP_KVDL_ENTRY_TYPE_IPV6_ADDRESS, 1, + &ipip_entry->dip_kvdl_index); + if (err) + return err; + + parms6 = mlxsw_sp_ipip_netdev_parms6(ipip_entry->ol_dev); + mlxsw_reg_rips_pack(rips_pl, ipip_entry->dip_kvdl_index, + &parms6.raddr); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rips), rips_pl); + if (err) + goto err_rips_write; + + return 0; + +err_rips_write: + mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_IPV6_ADDRESS, 1, + ipip_entry->dip_kvdl_index); + return err; +} + +static void +mlxsw_sp2_ipip_rem_addr_unset_gre6(struct mlxsw_sp *mlxsw_sp, + const struct mlxsw_sp_ipip_entry *ipip_entry) +{ + mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_IPV6_ADDRESS, 1, + ipip_entry->dip_kvdl_index); +} + +static const struct mlxsw_sp_ipip_ops mlxsw_sp2_ipip_gre6_ops = { + .dev_type = ARPHRD_IP6GRE, + .ul_proto = MLXSW_SP_L3_PROTO_IPV6, + .inc_parsing_depth = true, + .parms_init = mlxsw_sp2_ipip_netdev_parms_init_gre6, + .nexthop_update = mlxsw_sp2_ipip_nexthop_update_gre6, + .decap_config = mlxsw_sp2_ipip_decap_config_gre6, + .can_offload = mlxsw_sp2_ipip_can_offload_gre6, + .ol_loopback_config = mlxsw_sp2_ipip_ol_loopback_config_gre6, + .ol_netdev_change = mlxsw_sp2_ipip_ol_netdev_change_gre6, + .rem_ip_addr_set = mlxsw_sp2_ipip_rem_addr_set_gre6, + .rem_ip_addr_unset = mlxsw_sp2_ipip_rem_addr_unset_gre6, +}; + +const struct mlxsw_sp_ipip_ops *mlxsw_sp2_ipip_ops_arr[] = { + [MLXSW_SP_IPIP_TYPE_GRE4] = &mlxsw_sp_ipip_gre4_ops, + [MLXSW_SP_IPIP_TYPE_GRE6] = &mlxsw_sp2_ipip_gre6_ops, }; static int mlxsw_sp_ipip_ecn_encap_init_one(struct mlxsw_sp *mlxsw_sp, @@ -363,3 +676,22 @@ int mlxsw_sp_ipip_ecn_decap_init(struct mlxsw_sp *mlxsw_sp) return 0; } + +struct net_device * +mlxsw_sp_ipip_netdev_ul_dev_get(const struct net_device *ol_dev) +{ + struct net *net = dev_net(ol_dev); + struct ip_tunnel *tun4; + struct ip6_tnl *tun6; + + switch (ol_dev->type) { + case ARPHRD_IPGRE: + tun4 = netdev_priv(ol_dev); + return dev_get_by_index_rcu(net, tun4->parms.link); + case ARPHRD_IP6GRE: + tun6 = netdev_priv(ol_dev); + return dev_get_by_index_rcu(net, tun6->parms.link); + default: + return NULL; + } +} diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h index f0837b42d1d6..8cc259dcc8d0 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h @@ -7,6 +7,7 @@ #include "spectrum_router.h" #include <net/ip_fib.h> #include <linux/if_tunnel.h> +#include <net/ip6_tunnel.h> struct ip_tunnel_parm mlxsw_sp_ipip_netdev_parms4(const struct net_device *ol_dev); @@ -21,23 +22,36 @@ bool mlxsw_sp_l3addr_is_zero(union mlxsw_sp_l3addr addr); enum mlxsw_sp_ipip_type { MLXSW_SP_IPIP_TYPE_GRE4, + MLXSW_SP_IPIP_TYPE_GRE6, MLXSW_SP_IPIP_TYPE_MAX, }; +struct mlxsw_sp_ipip_parms { + enum mlxsw_sp_l3proto proto; + union mlxsw_sp_l3addr saddr; + union mlxsw_sp_l3addr daddr; + int link; + u32 ikey; + u32 okey; +}; + struct mlxsw_sp_ipip_entry { enum mlxsw_sp_ipip_type ipipt; struct net_device *ol_dev; /* Overlay. */ struct mlxsw_sp_rif_ipip_lb *ol_lb; struct mlxsw_sp_fib_entry *decap_fib_entry; struct list_head ipip_list_node; - union { - struct ip_tunnel_parm parms4; - }; + struct mlxsw_sp_ipip_parms parms; + u32 dip_kvdl_index; }; struct mlxsw_sp_ipip_ops { int dev_type; enum mlxsw_sp_l3proto ul_proto; /* Underlay. */ + bool inc_parsing_depth; + + struct mlxsw_sp_ipip_parms + (*parms_init)(const struct net_device *ol_dev); int (*nexthop_update)(struct mlxsw_sp *mlxsw_sp, u32 adj_index, struct mlxsw_sp_ipip_entry *ipip_entry, @@ -58,8 +72,13 @@ struct mlxsw_sp_ipip_ops { int (*ol_netdev_change)(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_ipip_entry *ipip_entry, struct netlink_ext_ack *extack); + int (*rem_ip_addr_set)(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_ipip_entry *ipip_entry); + void (*rem_ip_addr_unset)(struct mlxsw_sp *mlxsw_sp, + const struct mlxsw_sp_ipip_entry *ipip_entry); }; -extern const struct mlxsw_sp_ipip_ops *mlxsw_sp_ipip_ops_arr[]; +extern const struct mlxsw_sp_ipip_ops *mlxsw_sp1_ipip_ops_arr[]; +extern const struct mlxsw_sp_ipip_ops *mlxsw_sp2_ipip_ops_arr[]; #endif /* _MLXSW_IPIP_H_*/ diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c index 9958d503bf0e..4243d3b883ff 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c @@ -50,12 +50,24 @@ struct mlxsw_sp_qdisc_ops { struct mlxsw_sp_qdisc *(*find_class)(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, u32 parent); unsigned int num_classes; + + u8 (*get_prio_bitmap)(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, + struct mlxsw_sp_qdisc *child); + int (*get_tclass_num)(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, + struct mlxsw_sp_qdisc *child); +}; + +struct mlxsw_sp_qdisc_ets_band { + u8 prio_bitmap; + int tclass_num; +}; + +struct mlxsw_sp_qdisc_ets_data { + struct mlxsw_sp_qdisc_ets_band bands[IEEE_8021QAZ_MAX_TCS]; }; struct mlxsw_sp_qdisc { u32 handle; - int tclass_num; - u8 prio_bitmap; union { struct red_stats red; } xstats_base; @@ -67,6 +79,10 @@ struct mlxsw_sp_qdisc { u64 backlog; } stats_base; + union { + struct mlxsw_sp_qdisc_ets_data *ets_data; + }; + struct mlxsw_sp_qdisc_ops *ops; struct mlxsw_sp_qdisc *parent; struct mlxsw_sp_qdisc *qdiscs; @@ -141,8 +157,7 @@ mlxsw_sp_qdisc_walk_cb_find(struct mlxsw_sp_qdisc *qdisc, void *data) } static struct mlxsw_sp_qdisc * -mlxsw_sp_qdisc_find(struct mlxsw_sp_port *mlxsw_sp_port, u32 parent, - bool root_only) +mlxsw_sp_qdisc_find(struct mlxsw_sp_port *mlxsw_sp_port, u32 parent) { struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; @@ -150,8 +165,6 @@ mlxsw_sp_qdisc_find(struct mlxsw_sp_port *mlxsw_sp_port, u32 parent, return NULL; if (parent == TC_H_ROOT) return &qdisc_state->root_qdisc; - if (root_only) - return NULL; return mlxsw_sp_qdisc_walk(&qdisc_state->root_qdisc, mlxsw_sp_qdisc_walk_cb_find, &parent); } @@ -187,6 +200,32 @@ mlxsw_sp_qdisc_reduce_parent_backlog(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) tmp->stats_base.backlog -= mlxsw_sp_qdisc->stats_base.backlog; } +static u8 mlxsw_sp_qdisc_get_prio_bitmap(struct mlxsw_sp_port *mlxsw_sp_port, + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) +{ + struct mlxsw_sp_qdisc *parent = mlxsw_sp_qdisc->parent; + + if (!parent) + return 0xff; + if (!parent->ops->get_prio_bitmap) + return mlxsw_sp_qdisc_get_prio_bitmap(mlxsw_sp_port, parent); + return parent->ops->get_prio_bitmap(parent, mlxsw_sp_qdisc); +} + +#define MLXSW_SP_PORT_DEFAULT_TCLASS 0 + +static int mlxsw_sp_qdisc_get_tclass_num(struct mlxsw_sp_port *mlxsw_sp_port, + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) +{ + struct mlxsw_sp_qdisc *parent = mlxsw_sp_qdisc->parent; + + if (!parent) + return MLXSW_SP_PORT_DEFAULT_TCLASS; + if (!parent->ops->get_tclass_num) + return mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, parent); + return parent->ops->get_tclass_num(parent, mlxsw_sp_qdisc); +} + static int mlxsw_sp_qdisc_destroy(struct mlxsw_sp_port *mlxsw_sp_port, struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) @@ -194,6 +233,7 @@ mlxsw_sp_qdisc_destroy(struct mlxsw_sp_port *mlxsw_sp_port, struct mlxsw_sp_qdisc *root_qdisc = &mlxsw_sp_port->qdisc->root_qdisc; int err_hdroom = 0; int err = 0; + int i; if (!mlxsw_sp_qdisc) return 0; @@ -211,6 +251,9 @@ mlxsw_sp_qdisc_destroy(struct mlxsw_sp_port *mlxsw_sp_port, if (!mlxsw_sp_qdisc->ops) return 0; + for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++) + mlxsw_sp_qdisc_destroy(mlxsw_sp_port, + &mlxsw_sp_qdisc->qdiscs[i]); mlxsw_sp_qdisc_reduce_parent_backlog(mlxsw_sp_qdisc); if (mlxsw_sp_qdisc->ops->destroy) err = mlxsw_sp_qdisc->ops->destroy(mlxsw_sp_port, @@ -226,6 +269,87 @@ mlxsw_sp_qdisc_destroy(struct mlxsw_sp_port *mlxsw_sp_port, return err_hdroom ?: err; } +struct mlxsw_sp_qdisc_tree_validate { + bool forbid_ets; + bool forbid_root_tbf; + bool forbid_tbf; + bool forbid_red; +}; + +static int +__mlxsw_sp_qdisc_tree_validate(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, + struct mlxsw_sp_qdisc_tree_validate validate); + +static int +mlxsw_sp_qdisc_tree_validate_children(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, + struct mlxsw_sp_qdisc_tree_validate validate) +{ + unsigned int i; + int err; + + for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++) { + err = __mlxsw_sp_qdisc_tree_validate(&mlxsw_sp_qdisc->qdiscs[i], + validate); + if (err) + return err; + } + + return 0; +} + +static int +__mlxsw_sp_qdisc_tree_validate(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, + struct mlxsw_sp_qdisc_tree_validate validate) +{ + if (!mlxsw_sp_qdisc->ops) + return 0; + + switch (mlxsw_sp_qdisc->ops->type) { + case MLXSW_SP_QDISC_FIFO: + break; + case MLXSW_SP_QDISC_RED: + if (validate.forbid_red) + return -EINVAL; + validate.forbid_red = true; + validate.forbid_root_tbf = true; + validate.forbid_ets = true; + break; + case MLXSW_SP_QDISC_TBF: + if (validate.forbid_root_tbf) { + if (validate.forbid_tbf) + return -EINVAL; + /* This is a TC TBF. */ + validate.forbid_tbf = true; + validate.forbid_ets = true; + } else { + /* This is root TBF. */ + validate.forbid_root_tbf = true; + } + break; + case MLXSW_SP_QDISC_PRIO: + case MLXSW_SP_QDISC_ETS: + if (validate.forbid_ets) + return -EINVAL; + validate.forbid_root_tbf = true; + validate.forbid_ets = true; + break; + default: + WARN_ON(1); + return -EINVAL; + } + + return mlxsw_sp_qdisc_tree_validate_children(mlxsw_sp_qdisc, validate); +} + +static int mlxsw_sp_qdisc_tree_validate(struct mlxsw_sp_port *mlxsw_sp_port) +{ + struct mlxsw_sp_qdisc_tree_validate validate = {}; + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; + + mlxsw_sp_qdisc = &mlxsw_sp_port->qdisc->root_qdisc; + return __mlxsw_sp_qdisc_tree_validate(mlxsw_sp_qdisc, validate); +} + static int mlxsw_sp_qdisc_create(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, @@ -268,6 +392,10 @@ static int mlxsw_sp_qdisc_create(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_sp_qdisc->num_classes = ops->num_classes; mlxsw_sp_qdisc->ops = ops; mlxsw_sp_qdisc->handle = handle; + err = mlxsw_sp_qdisc_tree_validate(mlxsw_sp_port); + if (err) + goto err_replace; + err = ops->replace(mlxsw_sp_port, handle, mlxsw_sp_qdisc, params); if (err) goto err_replace; @@ -406,13 +534,17 @@ mlxsw_sp_qdisc_collect_tc_stats(struct mlxsw_sp_port *mlxsw_sp_port, u64 *p_tx_bytes, u64 *p_tx_packets, u64 *p_drops, u64 *p_backlog) { - int tclass_num = mlxsw_sp_qdisc->tclass_num; struct mlxsw_sp_port_xstats *xstats; u64 tx_bytes, tx_packets; + u8 prio_bitmap; + int tclass_num; + prio_bitmap = mlxsw_sp_qdisc_get_prio_bitmap(mlxsw_sp_port, + mlxsw_sp_qdisc); + tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, + mlxsw_sp_qdisc); xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; - mlxsw_sp_qdisc_bstats_per_priority_get(xstats, - mlxsw_sp_qdisc->prio_bitmap, + mlxsw_sp_qdisc_bstats_per_priority_get(xstats, prio_bitmap, &tx_packets, &tx_bytes); *p_tx_packets += tx_packets; @@ -506,19 +638,24 @@ static void mlxsw_sp_setup_tc_qdisc_red_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) { - int tclass_num = mlxsw_sp_qdisc->tclass_num; struct mlxsw_sp_qdisc_stats *stats_base; struct mlxsw_sp_port_xstats *xstats; struct red_stats *red_base; + u8 prio_bitmap; + int tclass_num; + prio_bitmap = mlxsw_sp_qdisc_get_prio_bitmap(mlxsw_sp_port, + mlxsw_sp_qdisc); + tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, + mlxsw_sp_qdisc); xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; stats_base = &mlxsw_sp_qdisc->stats_base; red_base = &mlxsw_sp_qdisc->xstats_base.red; - mlxsw_sp_qdisc_bstats_per_priority_get(xstats, - mlxsw_sp_qdisc->prio_bitmap, + mlxsw_sp_qdisc_bstats_per_priority_get(xstats, prio_bitmap, &stats_base->tx_packets, &stats_base->tx_bytes); + red_base->prob_mark = xstats->tc_ecn[tclass_num]; red_base->prob_drop = xstats->wred_drop[tclass_num]; red_base->pdrop = mlxsw_sp_xstats_tail_drop(xstats, tclass_num); @@ -532,8 +669,10 @@ static int mlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port *mlxsw_sp_port, struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) { - return mlxsw_sp_tclass_congestion_disable(mlxsw_sp_port, - mlxsw_sp_qdisc->tclass_num); + int tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, + mlxsw_sp_qdisc); + + return mlxsw_sp_tclass_congestion_disable(mlxsw_sp_port, tclass_num); } static int @@ -564,15 +703,33 @@ mlxsw_sp_qdisc_red_check_params(struct mlxsw_sp_port *mlxsw_sp_port, } static int +mlxsw_sp_qdisc_future_fifo_replace(struct mlxsw_sp_port *mlxsw_sp_port, + u32 handle, unsigned int band, + struct mlxsw_sp_qdisc *child_qdisc); +static void +mlxsw_sp_qdisc_future_fifos_init(struct mlxsw_sp_port *mlxsw_sp_port, + u32 handle); + +static int mlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params) { struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; struct tc_red_qopt_offload_params *p = params; - int tclass_num = mlxsw_sp_qdisc->tclass_num; + int tclass_num; u32 min, max; u64 prob; + int err; + + err = mlxsw_sp_qdisc_future_fifo_replace(mlxsw_sp_port, handle, 0, + &mlxsw_sp_qdisc->qdiscs[0]); + if (err) + return err; + mlxsw_sp_qdisc_future_fifos_init(mlxsw_sp_port, TC_H_UNSPEC); + + tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, + mlxsw_sp_qdisc); /* calculate probability in percentage */ prob = p->probability; @@ -615,22 +772,27 @@ mlxsw_sp_qdisc_get_red_xstats(struct mlxsw_sp_port *mlxsw_sp_port, void *xstats_ptr) { struct red_stats *xstats_base = &mlxsw_sp_qdisc->xstats_base.red; - int tclass_num = mlxsw_sp_qdisc->tclass_num; struct mlxsw_sp_port_xstats *xstats; struct red_stats *res = xstats_ptr; - int early_drops, pdrops; + int early_drops, marks, pdrops; + int tclass_num; + tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, + mlxsw_sp_qdisc); xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; early_drops = xstats->wred_drop[tclass_num] - xstats_base->prob_drop; + marks = xstats->tc_ecn[tclass_num] - xstats_base->prob_mark; pdrops = mlxsw_sp_xstats_tail_drop(xstats, tclass_num) - xstats_base->pdrop; res->pdrop += pdrops; res->prob_drop += early_drops; + res->prob_mark += marks; xstats_base->pdrop += pdrops; xstats_base->prob_drop += early_drops; + xstats_base->prob_mark += marks; return 0; } @@ -639,16 +801,19 @@ mlxsw_sp_qdisc_get_red_stats(struct mlxsw_sp_port *mlxsw_sp_port, struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, struct tc_qopt_offload_stats *stats_ptr) { - int tclass_num = mlxsw_sp_qdisc->tclass_num; struct mlxsw_sp_qdisc_stats *stats_base; struct mlxsw_sp_port_xstats *xstats; u64 overlimits; + int tclass_num; + tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, + mlxsw_sp_qdisc); xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; stats_base = &mlxsw_sp_qdisc->stats_base; mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, stats_ptr); - overlimits = xstats->wred_drop[tclass_num] - stats_base->overlimits; + overlimits = xstats->wred_drop[tclass_num] + + xstats->tc_ecn[tclass_num] - stats_base->overlimits; stats_ptr->qstats->overlimits += overlimits; stats_base->overlimits += overlimits; @@ -660,11 +825,12 @@ static struct mlxsw_sp_qdisc * mlxsw_sp_qdisc_leaf_find_class(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, u32 parent) { - return NULL; + /* RED and TBF are formally classful qdiscs, but all class references, + * including X:0, just refer to the same one class. + */ + return &mlxsw_sp_qdisc->qdiscs[0]; } -#define MLXSW_SP_PORT_DEFAULT_TCLASS 0 - static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_red = { .type = MLXSW_SP_QDISC_RED, .check_params = mlxsw_sp_qdisc_red_check_params, @@ -675,14 +841,19 @@ static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_red = { .get_xstats = mlxsw_sp_qdisc_get_red_xstats, .clean_stats = mlxsw_sp_setup_tc_qdisc_red_clean_stats, .find_class = mlxsw_sp_qdisc_leaf_find_class, + .num_classes = 1, }; +static int mlxsw_sp_qdisc_graft(struct mlxsw_sp_port *mlxsw_sp_port, + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, + u8 band, u32 child_handle); + static int __mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port, struct tc_red_qopt_offload *p) { struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; - mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false); + mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent); if (!mlxsw_sp_qdisc) return -EOPNOTSUPP; @@ -704,6 +875,9 @@ static int __mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port, case TC_RED_STATS: return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, &p->stats); + case TC_RED_GRAFT: + return mlxsw_sp_qdisc_graft(mlxsw_sp_port, mlxsw_sp_qdisc, 0, + p->child_handle); default: return -EOPNOTSUPP; } @@ -740,13 +914,34 @@ mlxsw_sp_setup_tc_qdisc_leaf_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_sp_qdisc->stats_base.backlog = 0; } +static enum mlxsw_reg_qeec_hr +mlxsw_sp_qdisc_tbf_hr(struct mlxsw_sp_port *mlxsw_sp_port, + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) +{ + if (mlxsw_sp_qdisc == &mlxsw_sp_port->qdisc->root_qdisc) + return MLXSW_REG_QEEC_HR_PORT; + + /* Configure subgroup shaper, so that both UC and MC traffic is subject + * to shaping. That is unlike RED, however UC queue lengths are going to + * be different than MC ones due to different pool and quota + * configurations, so the configuration is not applicable. For shaper on + * the other hand, subjecting the overall stream to the configured + * shaper makes sense. Also note that that is what we do for + * ieee_setmaxrate(). + */ + return MLXSW_REG_QEEC_HR_SUBGROUP; +} + static int mlxsw_sp_qdisc_tbf_destroy(struct mlxsw_sp_port *mlxsw_sp_port, struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) { - return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, - MLXSW_REG_QEEC_HR_SUBGROUP, - mlxsw_sp_qdisc->tclass_num, 0, + enum mlxsw_reg_qeec_hr hr = mlxsw_sp_qdisc_tbf_hr(mlxsw_sp_port, + mlxsw_sp_qdisc); + int tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, + mlxsw_sp_qdisc); + + return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, hr, tclass_num, 0, MLXSW_REG_QEEC_MAS_DIS, 0); } @@ -828,27 +1023,29 @@ mlxsw_sp_qdisc_tbf_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params) { + enum mlxsw_reg_qeec_hr hr = mlxsw_sp_qdisc_tbf_hr(mlxsw_sp_port, + mlxsw_sp_qdisc); struct tc_tbf_qopt_offload_replace_params *p = params; u64 rate_kbps = mlxsw_sp_qdisc_tbf_rate_kbps(p); + int tclass_num; u8 burst_size; int err; + err = mlxsw_sp_qdisc_future_fifo_replace(mlxsw_sp_port, handle, 0, + &mlxsw_sp_qdisc->qdiscs[0]); + if (err) + return err; + mlxsw_sp_qdisc_future_fifos_init(mlxsw_sp_port, TC_H_UNSPEC); + + tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, + mlxsw_sp_qdisc); + err = mlxsw_sp_qdisc_tbf_bs(mlxsw_sp_port, p->max_size, &burst_size); if (WARN_ON_ONCE(err)) /* check_params above was supposed to reject this value. */ return -EINVAL; - /* Configure subgroup shaper, so that both UC and MC traffic is subject - * to shaping. That is unlike RED, however UC queue lengths are going to - * be different than MC ones due to different pool and quota - * configurations, so the configuration is not applicable. For shaper on - * the other hand, subjecting the overall stream to the configured - * shaper makes sense. Also note that that is what we do for - * ieee_setmaxrate(). - */ - return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, - MLXSW_REG_QEEC_HR_SUBGROUP, - mlxsw_sp_qdisc->tclass_num, 0, + return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, hr, tclass_num, 0, rate_kbps, burst_size); } @@ -881,6 +1078,7 @@ static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_tbf = { .get_stats = mlxsw_sp_qdisc_get_tbf_stats, .clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats, .find_class = mlxsw_sp_qdisc_leaf_find_class, + .num_classes = 1, }; static int __mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port, @@ -888,7 +1086,7 @@ static int __mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port, { struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; - mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false); + mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent); if (!mlxsw_sp_qdisc) return -EOPNOTSUPP; @@ -907,6 +1105,9 @@ static int __mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port, case TC_TBF_STATS: return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, &p->stats); + case TC_TBF_GRAFT: + return mlxsw_sp_qdisc_graft(mlxsw_sp_port, mlxsw_sp_qdisc, 0, + p->child_handle); default: return -EOPNOTSUPP; } @@ -957,6 +1158,32 @@ static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_fifo = { .clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats, }; +static int +mlxsw_sp_qdisc_future_fifo_replace(struct mlxsw_sp_port *mlxsw_sp_port, + u32 handle, unsigned int band, + struct mlxsw_sp_qdisc *child_qdisc) +{ + struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; + + if (handle == qdisc_state->future_handle && + qdisc_state->future_fifos[band]) + return mlxsw_sp_qdisc_replace(mlxsw_sp_port, TC_H_UNSPEC, + child_qdisc, + &mlxsw_sp_qdisc_ops_fifo, + NULL); + return 0; +} + +static void +mlxsw_sp_qdisc_future_fifos_init(struct mlxsw_sp_port *mlxsw_sp_port, + u32 handle) +{ + struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; + + qdisc_state->future_handle = handle; + memset(qdisc_state->future_fifos, 0, sizeof(qdisc_state->future_fifos)); +} + static int __mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port, struct tc_fifo_qopt_offload *p) { @@ -965,16 +1192,15 @@ static int __mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port, unsigned int band; u32 parent_handle; - mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false); + mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent); if (!mlxsw_sp_qdisc && p->handle == TC_H_UNSPEC) { parent_handle = TC_H_MAJ(p->parent); if (parent_handle != qdisc_state->future_handle) { /* This notifications is for a different Qdisc than * previously. Wipe the future cache. */ - memset(qdisc_state->future_fifos, 0, - sizeof(qdisc_state->future_fifos)); - qdisc_state->future_handle = parent_handle; + mlxsw_sp_qdisc_future_fifos_init(mlxsw_sp_port, + parent_handle); } band = TC_H_MIN(p->parent) - 1; @@ -1033,11 +1259,10 @@ static int __mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_sp_port_ets_set(mlxsw_sp_port, MLXSW_REG_QEEC_HR_SUBGROUP, i, 0, false, 0); - mlxsw_sp_qdisc_destroy(mlxsw_sp_port, - &mlxsw_sp_qdisc->qdiscs[i]); - mlxsw_sp_qdisc->qdiscs[i].prio_bitmap = 0; } + kfree(mlxsw_sp_qdisc->ets_data); + mlxsw_sp_qdisc->ets_data = NULL; return 0; } @@ -1066,6 +1291,31 @@ mlxsw_sp_qdisc_prio_check_params(struct mlxsw_sp_port *mlxsw_sp_port, return __mlxsw_sp_qdisc_ets_check_params(p->bands); } +static struct mlxsw_sp_qdisc * +mlxsw_sp_qdisc_walk_cb_clean_stats(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, + void *mlxsw_sp_port) +{ + u64 backlog; + + if (mlxsw_sp_qdisc->ops) { + backlog = mlxsw_sp_qdisc->stats_base.backlog; + if (mlxsw_sp_qdisc->ops->clean_stats) + mlxsw_sp_qdisc->ops->clean_stats(mlxsw_sp_port, + mlxsw_sp_qdisc); + mlxsw_sp_qdisc->stats_base.backlog = backlog; + } + + return NULL; +} + +static void +mlxsw_sp_qdisc_tree_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) +{ + mlxsw_sp_qdisc_walk(mlxsw_sp_qdisc, mlxsw_sp_qdisc_walk_cb_clean_stats, + mlxsw_sp_port); +} + static int __mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, @@ -1074,69 +1324,80 @@ __mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, const unsigned int *weights, const u8 *priomap) { - struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; + struct mlxsw_sp_qdisc_ets_data *ets_data = mlxsw_sp_qdisc->ets_data; + struct mlxsw_sp_qdisc_ets_band *ets_band; struct mlxsw_sp_qdisc *child_qdisc; - int tclass, i, band, backlog; - u8 old_priomap; + u8 old_priomap, new_priomap; + int i, band; int err; + if (!ets_data) { + ets_data = kzalloc(sizeof(*ets_data), GFP_KERNEL); + if (!ets_data) + return -ENOMEM; + mlxsw_sp_qdisc->ets_data = ets_data; + + for (band = 0; band < mlxsw_sp_qdisc->num_classes; band++) { + int tclass_num = MLXSW_SP_PRIO_BAND_TO_TCLASS(band); + + ets_band = &ets_data->bands[band]; + ets_band->tclass_num = tclass_num; + } + } + for (band = 0; band < nbands; band++) { - tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band); + int tclass_num; + child_qdisc = &mlxsw_sp_qdisc->qdiscs[band]; - old_priomap = child_qdisc->prio_bitmap; - child_qdisc->prio_bitmap = 0; + ets_band = &ets_data->bands[band]; + + tclass_num = ets_band->tclass_num; + old_priomap = ets_band->prio_bitmap; + new_priomap = 0; err = mlxsw_sp_port_ets_set(mlxsw_sp_port, MLXSW_REG_QEEC_HR_SUBGROUP, - tclass, 0, !!quanta[band], + tclass_num, 0, !!quanta[band], weights[band]); if (err) return err; for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { if (priomap[i] == band) { - child_qdisc->prio_bitmap |= BIT(i); + new_priomap |= BIT(i); if (BIT(i) & old_priomap) continue; err = mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, - i, tclass); + i, tclass_num); if (err) return err; } } - child_qdisc->tclass_num = tclass; + ets_band->prio_bitmap = new_priomap; - if (old_priomap != child_qdisc->prio_bitmap && - child_qdisc->ops && child_qdisc->ops->clean_stats) { - backlog = child_qdisc->stats_base.backlog; - child_qdisc->ops->clean_stats(mlxsw_sp_port, - child_qdisc); - child_qdisc->stats_base.backlog = backlog; - } + if (old_priomap != new_priomap) + mlxsw_sp_qdisc_tree_clean_stats(mlxsw_sp_port, + child_qdisc); - if (handle == qdisc_state->future_handle && - qdisc_state->future_fifos[band]) { - err = mlxsw_sp_qdisc_replace(mlxsw_sp_port, TC_H_UNSPEC, - child_qdisc, - &mlxsw_sp_qdisc_ops_fifo, - NULL); - if (err) - return err; - } + err = mlxsw_sp_qdisc_future_fifo_replace(mlxsw_sp_port, handle, + band, child_qdisc); + if (err) + return err; } for (; band < IEEE_8021QAZ_MAX_TCS; band++) { - tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band); + ets_band = &ets_data->bands[band]; + ets_band->prio_bitmap = 0; + child_qdisc = &mlxsw_sp_qdisc->qdiscs[band]; - child_qdisc->prio_bitmap = 0; mlxsw_sp_qdisc_destroy(mlxsw_sp_port, child_qdisc); + mlxsw_sp_port_ets_set(mlxsw_sp_port, MLXSW_REG_QEEC_HR_SUBGROUP, - tclass, 0, false, 0); + ets_band->tclass_num, 0, false, 0); } - qdisc_state->future_handle = TC_H_UNSPEC; - memset(qdisc_state->future_fifos, 0, sizeof(qdisc_state->future_fifos)); + mlxsw_sp_qdisc_future_fifos_init(mlxsw_sp_port, TC_H_UNSPEC); return 0; } @@ -1238,6 +1499,31 @@ mlxsw_sp_qdisc_prio_find_class(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, return &mlxsw_sp_qdisc->qdiscs[band]; } +static struct mlxsw_sp_qdisc_ets_band * +mlxsw_sp_qdisc_ets_get_band(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, + struct mlxsw_sp_qdisc *child) +{ + unsigned int band = child - mlxsw_sp_qdisc->qdiscs; + + if (WARN_ON(band >= IEEE_8021QAZ_MAX_TCS)) + band = 0; + return &mlxsw_sp_qdisc->ets_data->bands[band]; +} + +static u8 +mlxsw_sp_qdisc_ets_get_prio_bitmap(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, + struct mlxsw_sp_qdisc *child) +{ + return mlxsw_sp_qdisc_ets_get_band(mlxsw_sp_qdisc, child)->prio_bitmap; +} + +static int +mlxsw_sp_qdisc_ets_get_tclass_num(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, + struct mlxsw_sp_qdisc *child) +{ + return mlxsw_sp_qdisc_ets_get_band(mlxsw_sp_qdisc, child)->tclass_num; +} + static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = { .type = MLXSW_SP_QDISC_PRIO, .check_params = mlxsw_sp_qdisc_prio_check_params, @@ -1248,6 +1534,8 @@ static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = { .clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats, .find_class = mlxsw_sp_qdisc_prio_find_class, .num_classes = IEEE_8021QAZ_MAX_TCS, + .get_prio_bitmap = mlxsw_sp_qdisc_ets_get_prio_bitmap, + .get_tclass_num = mlxsw_sp_qdisc_ets_get_tclass_num, }; static int @@ -1299,6 +1587,8 @@ static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_ets = { .clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats, .find_class = mlxsw_sp_qdisc_prio_find_class, .num_classes = IEEE_8021QAZ_MAX_TCS, + .get_prio_bitmap = mlxsw_sp_qdisc_ets_get_prio_bitmap, + .get_tclass_num = mlxsw_sp_qdisc_ets_get_tclass_num, }; /* Linux allows linking of Qdiscs to arbitrary classes (so long as the resulting @@ -1326,10 +1616,9 @@ static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_ets = { * grafted corresponds to the parent handle. If the two don't match, we * unoffload the child. */ -static int -__mlxsw_sp_qdisc_ets_graft(struct mlxsw_sp_port *mlxsw_sp_port, - struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, - u8 band, u32 child_handle) +static int mlxsw_sp_qdisc_graft(struct mlxsw_sp_port *mlxsw_sp_port, + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, + u8 band, u32 child_handle) { struct mlxsw_sp_qdisc *old_qdisc; u32 parent; @@ -1362,21 +1651,12 @@ __mlxsw_sp_qdisc_ets_graft(struct mlxsw_sp_port *mlxsw_sp_port, return -EOPNOTSUPP; } -static int -mlxsw_sp_qdisc_prio_graft(struct mlxsw_sp_port *mlxsw_sp_port, - struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, - struct tc_prio_qopt_offload_graft_params *p) -{ - return __mlxsw_sp_qdisc_ets_graft(mlxsw_sp_port, mlxsw_sp_qdisc, - p->band, p->child_handle); -} - static int __mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port, struct tc_prio_qopt_offload *p) { struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; - mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, true); + mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent); if (!mlxsw_sp_qdisc) return -EOPNOTSUPP; @@ -1396,8 +1676,9 @@ static int __mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port, return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, &p->stats); case TC_PRIO_GRAFT: - return mlxsw_sp_qdisc_prio_graft(mlxsw_sp_port, mlxsw_sp_qdisc, - &p->graft_params); + return mlxsw_sp_qdisc_graft(mlxsw_sp_port, mlxsw_sp_qdisc, + p->graft_params.band, + p->graft_params.child_handle); default: return -EOPNOTSUPP; } @@ -1420,7 +1701,7 @@ static int __mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port, { struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; - mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, true); + mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent); if (!mlxsw_sp_qdisc) return -EOPNOTSUPP; @@ -1440,9 +1721,9 @@ static int __mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port, return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, &p->stats); case TC_ETS_GRAFT: - return __mlxsw_sp_qdisc_ets_graft(mlxsw_sp_port, mlxsw_sp_qdisc, - p->graft_params.band, - p->graft_params.child_handle); + return mlxsw_sp_qdisc_graft(mlxsw_sp_port, mlxsw_sp_qdisc, + p->graft_params.band, + p->graft_params.child_handle); default: return -EOPNOTSUPP; } @@ -1472,6 +1753,7 @@ struct mlxsw_sp_qevent_binding { u32 handle; int tclass_num; enum mlxsw_sp_span_trigger span_trigger; + unsigned int action_mask; }; static LIST_HEAD(mlxsw_sp_qevent_block_cb_list); @@ -1482,8 +1764,10 @@ static int mlxsw_sp_qevent_span_configure(struct mlxsw_sp *mlxsw_sp, const struct mlxsw_sp_span_agent_parms *agent_parms, int *p_span_id) { + enum mlxsw_sp_span_trigger span_trigger = qevent_binding->span_trigger; struct mlxsw_sp_port *mlxsw_sp_port = qevent_binding->mlxsw_sp_port; struct mlxsw_sp_span_trigger_parms trigger_parms = {}; + bool ingress; int span_id; int err; @@ -1491,18 +1775,19 @@ static int mlxsw_sp_qevent_span_configure(struct mlxsw_sp *mlxsw_sp, if (err) return err; - err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port, true); + ingress = mlxsw_sp_span_trigger_is_ingress(span_trigger); + err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port, ingress); if (err) goto err_analyzed_port_get; trigger_parms.span_id = span_id; trigger_parms.probability_rate = 1; - err = mlxsw_sp_span_agent_bind(mlxsw_sp, qevent_binding->span_trigger, mlxsw_sp_port, + err = mlxsw_sp_span_agent_bind(mlxsw_sp, span_trigger, mlxsw_sp_port, &trigger_parms); if (err) goto err_agent_bind; - err = mlxsw_sp_span_trigger_enable(mlxsw_sp_port, qevent_binding->span_trigger, + err = mlxsw_sp_span_trigger_enable(mlxsw_sp_port, span_trigger, qevent_binding->tclass_num); if (err) goto err_trigger_enable; @@ -1511,10 +1796,10 @@ static int mlxsw_sp_qevent_span_configure(struct mlxsw_sp *mlxsw_sp, return 0; err_trigger_enable: - mlxsw_sp_span_agent_unbind(mlxsw_sp, qevent_binding->span_trigger, mlxsw_sp_port, + mlxsw_sp_span_agent_unbind(mlxsw_sp, span_trigger, mlxsw_sp_port, &trigger_parms); err_agent_bind: - mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, true); + mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, ingress); err_analyzed_port_get: mlxsw_sp_span_agent_put(mlxsw_sp, span_id); return err; @@ -1524,16 +1809,20 @@ static void mlxsw_sp_qevent_span_deconfigure(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_qevent_binding *qevent_binding, int span_id) { + enum mlxsw_sp_span_trigger span_trigger = qevent_binding->span_trigger; struct mlxsw_sp_port *mlxsw_sp_port = qevent_binding->mlxsw_sp_port; struct mlxsw_sp_span_trigger_parms trigger_parms = { .span_id = span_id, }; + bool ingress; + + ingress = mlxsw_sp_span_trigger_is_ingress(span_trigger); - mlxsw_sp_span_trigger_disable(mlxsw_sp_port, qevent_binding->span_trigger, + mlxsw_sp_span_trigger_disable(mlxsw_sp_port, span_trigger, qevent_binding->tclass_num); - mlxsw_sp_span_agent_unbind(mlxsw_sp, qevent_binding->span_trigger, mlxsw_sp_port, + mlxsw_sp_span_agent_unbind(mlxsw_sp, span_trigger, mlxsw_sp_port, &trigger_parms); - mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, true); + mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, ingress); mlxsw_sp_span_agent_put(mlxsw_sp, span_id); } @@ -1583,10 +1872,17 @@ static void mlxsw_sp_qevent_trap_deconfigure(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_qevent_span_deconfigure(mlxsw_sp, qevent_binding, mall_entry->trap.span_id); } -static int mlxsw_sp_qevent_entry_configure(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_mall_entry *mall_entry, - struct mlxsw_sp_qevent_binding *qevent_binding) +static int +mlxsw_sp_qevent_entry_configure(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_mall_entry *mall_entry, + struct mlxsw_sp_qevent_binding *qevent_binding, + struct netlink_ext_ack *extack) { + if (!(BIT(mall_entry->type) & qevent_binding->action_mask)) { + NL_SET_ERR_MSG(extack, "Action not supported at this qevent"); + return -EOPNOTSUPP; + } + switch (mall_entry->type) { case MLXSW_SP_MALL_ACTION_TYPE_MIRROR: return mlxsw_sp_qevent_mirror_configure(mlxsw_sp, mall_entry, qevent_binding); @@ -1614,15 +1910,17 @@ static void mlxsw_sp_qevent_entry_deconfigure(struct mlxsw_sp *mlxsw_sp, } } -static int mlxsw_sp_qevent_binding_configure(struct mlxsw_sp_qevent_block *qevent_block, - struct mlxsw_sp_qevent_binding *qevent_binding) +static int +mlxsw_sp_qevent_binding_configure(struct mlxsw_sp_qevent_block *qevent_block, + struct mlxsw_sp_qevent_binding *qevent_binding, + struct netlink_ext_ack *extack) { struct mlxsw_sp_mall_entry *mall_entry; int err; list_for_each_entry(mall_entry, &qevent_block->mall_entry_list, list) { err = mlxsw_sp_qevent_entry_configure(qevent_block->mlxsw_sp, mall_entry, - qevent_binding); + qevent_binding, extack); if (err) goto err_entry_configure; } @@ -1646,13 +1944,17 @@ static void mlxsw_sp_qevent_binding_deconfigure(struct mlxsw_sp_qevent_block *qe qevent_binding); } -static int mlxsw_sp_qevent_block_configure(struct mlxsw_sp_qevent_block *qevent_block) +static int +mlxsw_sp_qevent_block_configure(struct mlxsw_sp_qevent_block *qevent_block, + struct netlink_ext_ack *extack) { struct mlxsw_sp_qevent_binding *qevent_binding; int err; list_for_each_entry(qevent_binding, &qevent_block->binding_list, list) { - err = mlxsw_sp_qevent_binding_configure(qevent_block, qevent_binding); + err = mlxsw_sp_qevent_binding_configure(qevent_block, + qevent_binding, + extack); if (err) goto err_binding_configure; } @@ -1737,7 +2039,7 @@ static int mlxsw_sp_qevent_mall_replace(struct mlxsw_sp *mlxsw_sp, list_add_tail(&mall_entry->list, &qevent_block->mall_entry_list); - err = mlxsw_sp_qevent_block_configure(qevent_block); + err = mlxsw_sp_qevent_block_configure(qevent_block, f->common.extack); if (err) goto err_block_configure; @@ -1825,7 +2127,8 @@ static void mlxsw_sp_qevent_block_release(void *cb_priv) static struct mlxsw_sp_qevent_binding * mlxsw_sp_qevent_binding_create(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, int tclass_num, - enum mlxsw_sp_span_trigger span_trigger) + enum mlxsw_sp_span_trigger span_trigger, + unsigned int action_mask) { struct mlxsw_sp_qevent_binding *binding; @@ -1837,6 +2140,7 @@ mlxsw_sp_qevent_binding_create(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, binding->handle = handle; binding->tclass_num = tclass_num; binding->span_trigger = span_trigger; + binding->action_mask = action_mask; return binding; } @@ -1862,9 +2166,11 @@ mlxsw_sp_qevent_binding_lookup(struct mlxsw_sp_qevent_block *block, return NULL; } -static int mlxsw_sp_setup_tc_block_qevent_bind(struct mlxsw_sp_port *mlxsw_sp_port, - struct flow_block_offload *f, - enum mlxsw_sp_span_trigger span_trigger) +static int +mlxsw_sp_setup_tc_block_qevent_bind(struct mlxsw_sp_port *mlxsw_sp_port, + struct flow_block_offload *f, + enum mlxsw_sp_span_trigger span_trigger, + unsigned int action_mask) { struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; struct mlxsw_sp_qevent_binding *qevent_binding; @@ -1872,6 +2178,7 @@ static int mlxsw_sp_setup_tc_block_qevent_bind(struct mlxsw_sp_port *mlxsw_sp_po struct flow_block_cb *block_cb; struct mlxsw_sp_qdisc *qdisc; bool register_block = false; + int tclass_num; int err; block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_qevent_block_cb, mlxsw_sp); @@ -1904,14 +2211,19 @@ static int mlxsw_sp_setup_tc_block_qevent_bind(struct mlxsw_sp_port *mlxsw_sp_po goto err_binding_exists; } - qevent_binding = mlxsw_sp_qevent_binding_create(mlxsw_sp_port, f->sch->handle, - qdisc->tclass_num, span_trigger); + tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, qdisc); + qevent_binding = mlxsw_sp_qevent_binding_create(mlxsw_sp_port, + f->sch->handle, + tclass_num, + span_trigger, + action_mask); if (IS_ERR(qevent_binding)) { err = PTR_ERR(qevent_binding); goto err_binding_create; } - err = mlxsw_sp_qevent_binding_configure(qevent_block, qevent_binding); + err = mlxsw_sp_qevent_binding_configure(qevent_block, qevent_binding, + f->extack); if (err) goto err_binding_configure; @@ -1963,15 +2275,19 @@ static void mlxsw_sp_setup_tc_block_qevent_unbind(struct mlxsw_sp_port *mlxsw_sp } } -static int mlxsw_sp_setup_tc_block_qevent(struct mlxsw_sp_port *mlxsw_sp_port, - struct flow_block_offload *f, - enum mlxsw_sp_span_trigger span_trigger) +static int +mlxsw_sp_setup_tc_block_qevent(struct mlxsw_sp_port *mlxsw_sp_port, + struct flow_block_offload *f, + enum mlxsw_sp_span_trigger span_trigger, + unsigned int action_mask) { f->driver_block_list = &mlxsw_sp_qevent_block_cb_list; switch (f->command) { case FLOW_BLOCK_BIND: - return mlxsw_sp_setup_tc_block_qevent_bind(mlxsw_sp_port, f, span_trigger); + return mlxsw_sp_setup_tc_block_qevent_bind(mlxsw_sp_port, f, + span_trigger, + action_mask); case FLOW_BLOCK_UNBIND: mlxsw_sp_setup_tc_block_qevent_unbind(mlxsw_sp_port, f, span_trigger); return 0; @@ -1983,7 +2299,22 @@ static int mlxsw_sp_setup_tc_block_qevent(struct mlxsw_sp_port *mlxsw_sp_port, int mlxsw_sp_setup_tc_block_qevent_early_drop(struct mlxsw_sp_port *mlxsw_sp_port, struct flow_block_offload *f) { - return mlxsw_sp_setup_tc_block_qevent(mlxsw_sp_port, f, MLXSW_SP_SPAN_TRIGGER_EARLY_DROP); + unsigned int action_mask = BIT(MLXSW_SP_MALL_ACTION_TYPE_MIRROR) | + BIT(MLXSW_SP_MALL_ACTION_TYPE_TRAP); + + return mlxsw_sp_setup_tc_block_qevent(mlxsw_sp_port, f, + MLXSW_SP_SPAN_TRIGGER_EARLY_DROP, + action_mask); +} + +int mlxsw_sp_setup_tc_block_qevent_mark(struct mlxsw_sp_port *mlxsw_sp_port, + struct flow_block_offload *f) +{ + unsigned int action_mask = BIT(MLXSW_SP_MALL_ACTION_TYPE_MIRROR); + + return mlxsw_sp_setup_tc_block_qevent(mlxsw_sp_port, f, + MLXSW_SP_SPAN_TRIGGER_ECN, + action_mask); } int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port) @@ -1995,8 +2326,6 @@ int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port) return -ENOMEM; mutex_init(&qdisc_state->lock); - qdisc_state->root_qdisc.prio_bitmap = 0xff; - qdisc_state->root_qdisc.tclass_num = MLXSW_SP_PORT_DEFAULT_TCLASS; mlxsw_sp_port->qdisc = qdisc_state; return 0; } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 19bb3ca0515e..217e3b351dfe 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -57,6 +57,7 @@ struct mlxsw_sp_rif { unsigned char addr[ETH_ALEN]; int mtu; u16 rif_index; + u8 mac_profile_id; u16 vr_id; const struct mlxsw_sp_rif_ops *ops; struct mlxsw_sp *mlxsw_sp; @@ -106,15 +107,23 @@ struct mlxsw_sp_rif_ops { void (*setup)(struct mlxsw_sp_rif *rif, const struct mlxsw_sp_rif_params *params); - int (*configure)(struct mlxsw_sp_rif *rif); + int (*configure)(struct mlxsw_sp_rif *rif, + struct netlink_ext_ack *extack); void (*deconfigure)(struct mlxsw_sp_rif *rif); struct mlxsw_sp_fid * (*fid_get)(struct mlxsw_sp_rif *rif, struct netlink_ext_ack *extack); void (*fdb_del)(struct mlxsw_sp_rif *rif, const char *mac); }; +struct mlxsw_sp_rif_mac_profile { + unsigned char mac_prefix[ETH_ALEN]; + refcount_t ref_count; + u8 id; +}; + struct mlxsw_sp_router_ops { int (*init)(struct mlxsw_sp *mlxsw_sp); + int (*ipips_init)(struct mlxsw_sp *mlxsw_sp); }; static struct mlxsw_sp_rif * @@ -1055,22 +1064,13 @@ static void mlxsw_sp_vrs_fini(struct mlxsw_sp *mlxsw_sp) kfree(mlxsw_sp->router->vrs); } -static struct net_device * -__mlxsw_sp_ipip_netdev_ul_dev_get(const struct net_device *ol_dev) -{ - struct ip_tunnel *tun = netdev_priv(ol_dev); - struct net *net = dev_net(ol_dev); - - return dev_get_by_index_rcu(net, tun->parms.link); -} - u32 mlxsw_sp_ipip_dev_ul_tb_id(const struct net_device *ol_dev) { struct net_device *d; u32 tb_id; rcu_read_lock(); - d = __mlxsw_sp_ipip_netdev_ul_dev_get(ol_dev); + d = mlxsw_sp_ipip_netdev_ul_dev_get(ol_dev); if (d) tb_id = l3mdev_fib_table(d) ? : RT_TABLE_MAIN; else @@ -1116,6 +1116,7 @@ mlxsw_sp_ipip_entry_alloc(struct mlxsw_sp *mlxsw_sp, const struct mlxsw_sp_ipip_ops *ipip_ops; struct mlxsw_sp_ipip_entry *ipip_entry; struct mlxsw_sp_ipip_entry *ret = NULL; + int err; ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipipt]; ipip_entry = kzalloc(sizeof(*ipip_entry), GFP_KERNEL); @@ -1131,26 +1132,30 @@ mlxsw_sp_ipip_entry_alloc(struct mlxsw_sp *mlxsw_sp, ipip_entry->ipipt = ipipt; ipip_entry->ol_dev = ol_dev; + ipip_entry->parms = ipip_ops->parms_init(ol_dev); - switch (ipip_ops->ul_proto) { - case MLXSW_SP_L3_PROTO_IPV4: - ipip_entry->parms4 = mlxsw_sp_ipip_netdev_parms4(ol_dev); - break; - case MLXSW_SP_L3_PROTO_IPV6: - WARN_ON(1); - break; + err = ipip_ops->rem_ip_addr_set(mlxsw_sp, ipip_entry); + if (err) { + ret = ERR_PTR(err); + goto err_rem_ip_addr_set; } return ipip_entry; +err_rem_ip_addr_set: + mlxsw_sp_rif_destroy(&ipip_entry->ol_lb->common); err_ol_ipip_lb_create: kfree(ipip_entry); return ret; } -static void -mlxsw_sp_ipip_entry_dealloc(struct mlxsw_sp_ipip_entry *ipip_entry) +static void mlxsw_sp_ipip_entry_dealloc(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_ipip_entry *ipip_entry) { + const struct mlxsw_sp_ipip_ops *ipip_ops = + mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt]; + + ipip_ops->rem_ip_addr_unset(mlxsw_sp, ipip_entry); mlxsw_sp_rif_destroy(&ipip_entry->ol_lb->common); kfree(ipip_entry); } @@ -1174,6 +1179,32 @@ mlxsw_sp_ipip_entry_saddr_matches(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_l3addr_eq(&tun_saddr, &saddr); } +static int mlxsw_sp_ipip_decap_parsing_depth_inc(struct mlxsw_sp *mlxsw_sp, + enum mlxsw_sp_ipip_type ipipt) +{ + const struct mlxsw_sp_ipip_ops *ipip_ops; + + ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipipt]; + + /* Not all tunnels require to increase the default pasing depth + * (96 bytes). + */ + if (ipip_ops->inc_parsing_depth) + return mlxsw_sp_parsing_depth_inc(mlxsw_sp); + + return 0; +} + +static void mlxsw_sp_ipip_decap_parsing_depth_dec(struct mlxsw_sp *mlxsw_sp, + enum mlxsw_sp_ipip_type ipipt) +{ + const struct mlxsw_sp_ipip_ops *ipip_ops = + mlxsw_sp->router->ipip_ops_arr[ipipt]; + + if (ipip_ops->inc_parsing_depth) + mlxsw_sp_parsing_depth_dec(mlxsw_sp); +} + static int mlxsw_sp_fib_entry_decap_init(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_entry *fib_entry, @@ -1187,18 +1218,32 @@ mlxsw_sp_fib_entry_decap_init(struct mlxsw_sp *mlxsw_sp, if (err) return err; + err = mlxsw_sp_ipip_decap_parsing_depth_inc(mlxsw_sp, + ipip_entry->ipipt); + if (err) + goto err_parsing_depth_inc; + ipip_entry->decap_fib_entry = fib_entry; fib_entry->decap.ipip_entry = ipip_entry; fib_entry->decap.tunnel_index = tunnel_index; + return 0; + +err_parsing_depth_inc: + mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1, + fib_entry->decap.tunnel_index); + return err; } static void mlxsw_sp_fib_entry_decap_fini(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_entry *fib_entry) { + enum mlxsw_sp_ipip_type ipipt = fib_entry->decap.ipip_entry->ipipt; + /* Unlink this node from the IPIP entry that it's the decap entry of. */ fib_entry->decap.ipip_entry->decap_fib_entry = NULL; fib_entry->decap.ipip_entry = NULL; + mlxsw_sp_ipip_decap_parsing_depth_dec(mlxsw_sp, ipipt); mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1, fib_entry->decap.tunnel_index); } @@ -1309,6 +1354,11 @@ mlxsw_sp_ipip_entry_find_decap(struct mlxsw_sp *mlxsw_sp, saddr_len = 4; saddr_prefix_len = 32; break; + case MLXSW_SP_L3_PROTO_IPV6: + saddrp = &saddr.addr6; + saddr_len = 16; + saddr_prefix_len = 128; + break; default: WARN_ON(1); return NULL; @@ -1345,7 +1395,7 @@ mlxsw_sp_ipip_entry_destroy(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_ipip_entry *ipip_entry) { list_del(&ipip_entry->ipip_list_node); - mlxsw_sp_ipip_entry_dealloc(ipip_entry); + mlxsw_sp_ipip_entry_dealloc(mlxsw_sp, ipip_entry); } static bool @@ -1450,7 +1500,7 @@ mlxsw_sp_ipip_entry_find_by_ul_dev(const struct mlxsw_sp *mlxsw_sp, struct net_device *ipip_ul_dev; rcu_read_lock(); - ipip_ul_dev = __mlxsw_sp_ipip_netdev_ul_dev_get(ol_dev); + ipip_ul_dev = mlxsw_sp_ipip_netdev_ul_dev_get(ol_dev); rcu_read_unlock(); if (ipip_ul_dev == ul_dev) @@ -1536,23 +1586,34 @@ mlxsw_sp_rif_ipip_lb_op(struct mlxsw_sp_rif_ipip_lb *lb_rif, u16 ul_vr_id, u16 ul_rif_id, bool enable) { struct mlxsw_sp_rif_ipip_lb_config lb_cf = lb_rif->lb_config; + enum mlxsw_reg_ritr_loopback_ipip_options ipip_options; struct mlxsw_sp_rif *rif = &lb_rif->common; struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; char ritr_pl[MLXSW_REG_RITR_LEN]; + struct in6_addr *saddr6; u32 saddr4; + ipip_options = MLXSW_REG_RITR_LOOPBACK_IPIP_OPTIONS_GRE_KEY_PRESET; switch (lb_cf.ul_protocol) { case MLXSW_SP_L3_PROTO_IPV4: saddr4 = be32_to_cpu(lb_cf.saddr.addr4); mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_LOOPBACK_IF, rif->rif_index, rif->vr_id, rif->dev->mtu); mlxsw_reg_ritr_loopback_ipip4_pack(ritr_pl, lb_cf.lb_ipipt, - MLXSW_REG_RITR_LOOPBACK_IPIP_OPTIONS_GRE_KEY_PRESET, - ul_vr_id, ul_rif_id, saddr4, lb_cf.okey); + ipip_options, ul_vr_id, + ul_rif_id, saddr4, + lb_cf.okey); break; case MLXSW_SP_L3_PROTO_IPV6: - return -EAFNOSUPPORT; + saddr6 = &lb_cf.saddr.addr6; + mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_LOOPBACK_IF, + rif->rif_index, rif->vr_id, rif->dev->mtu); + mlxsw_reg_ritr_loopback_ipip6_pack(ritr_pl, lb_cf.lb_ipipt, + ipip_options, ul_vr_id, + ul_rif_id, saddr6, + lb_cf.okey); + break; } return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); @@ -1827,7 +1888,7 @@ static void mlxsw_sp_ipip_demote_tunnel_by_ul_netdev(struct mlxsw_sp *mlxsw_sp, struct net_device *ipip_ul_dev; rcu_read_lock(); - ipip_ul_dev = __mlxsw_sp_ipip_netdev_ul_dev_get(ol_dev); + ipip_ul_dev = mlxsw_sp_ipip_netdev_ul_dev_get(ol_dev); rcu_read_unlock(); if (ipip_ul_dev == ul_dev) mlxsw_sp_ipip_entry_demote_tunnel(mlxsw_sp, ipip_entry); @@ -4152,7 +4213,7 @@ static bool mlxsw_sp_ipip_netdev_ul_up(struct net_device *ol_dev) bool is_up; rcu_read_lock(); - ul_dev = __mlxsw_sp_ipip_netdev_ul_dev_get(ol_dev); + ul_dev = mlxsw_sp_ipip_netdev_ul_dev_get(ol_dev); is_up = ul_dev ? (ul_dev->flags & IFF_UP) : true; rcu_read_unlock(); @@ -4376,6 +4437,66 @@ static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp, } } +static int mlxsw_sp_adj_trap_entry_init(struct mlxsw_sp *mlxsw_sp) +{ + enum mlxsw_reg_ratr_trap_action trap_action; + char ratr_pl[MLXSW_REG_RATR_LEN]; + int err; + + err = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1, + &mlxsw_sp->router->adj_trap_index); + if (err) + return err; + + trap_action = MLXSW_REG_RATR_TRAP_ACTION_TRAP; + mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY, true, + MLXSW_REG_RATR_TYPE_ETHERNET, + mlxsw_sp->router->adj_trap_index, + mlxsw_sp->router->lb_rif_index); + mlxsw_reg_ratr_trap_action_set(ratr_pl, trap_action); + mlxsw_reg_ratr_trap_id_set(ratr_pl, MLXSW_TRAP_ID_RTR_EGRESS0); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl); + if (err) + goto err_ratr_write; + + return 0; + +err_ratr_write: + mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1, + mlxsw_sp->router->adj_trap_index); + return err; +} + +static void mlxsw_sp_adj_trap_entry_fini(struct mlxsw_sp *mlxsw_sp) +{ + mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1, + mlxsw_sp->router->adj_trap_index); +} + +static int mlxsw_sp_nexthop_group_inc(struct mlxsw_sp *mlxsw_sp) +{ + int err; + + if (refcount_inc_not_zero(&mlxsw_sp->router->num_groups)) + return 0; + + err = mlxsw_sp_adj_trap_entry_init(mlxsw_sp); + if (err) + return err; + + refcount_set(&mlxsw_sp->router->num_groups, 1); + + return 0; +} + +static void mlxsw_sp_nexthop_group_dec(struct mlxsw_sp *mlxsw_sp) +{ + if (!refcount_dec_and_test(&mlxsw_sp->router->num_groups)) + return; + + mlxsw_sp_adj_trap_entry_fini(mlxsw_sp); +} + static void mlxsw_sp_nh_grp_activity_get(struct mlxsw_sp *mlxsw_sp, const struct mlxsw_sp_nexthop_group *nh_grp, @@ -4790,6 +4911,9 @@ mlxsw_sp_nexthop_obj_group_info_init(struct mlxsw_sp *mlxsw_sp, if (err) goto err_nexthop_obj_init; } + err = mlxsw_sp_nexthop_group_inc(mlxsw_sp); + if (err) + goto err_group_inc; err = mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp); if (err) { NL_SET_ERR_MSG_MOD(info->extack, "Failed to write adjacency entries to the device"); @@ -4808,6 +4932,8 @@ mlxsw_sp_nexthop_obj_group_info_init(struct mlxsw_sp *mlxsw_sp, return 0; err_group_refresh: + mlxsw_sp_nexthop_group_dec(mlxsw_sp); +err_group_inc: i = nhgi->count; err_nexthop_obj_init: for (i--; i >= 0; i--) { @@ -4832,6 +4958,7 @@ mlxsw_sp_nexthop_obj_group_info_fini(struct mlxsw_sp *mlxsw_sp, cancel_delayed_work(&router->nh_grp_activity_dw); } + mlxsw_sp_nexthop_group_dec(mlxsw_sp); for (i = nhgi->count - 1; i >= 0; i--) { struct mlxsw_sp_nexthop *nh = &nhgi->nexthops[i]; @@ -5223,6 +5350,9 @@ mlxsw_sp_nexthop4_group_info_init(struct mlxsw_sp *mlxsw_sp, if (err) goto err_nexthop4_init; } + err = mlxsw_sp_nexthop_group_inc(mlxsw_sp); + if (err) + goto err_group_inc; err = mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp); if (err) goto err_group_refresh; @@ -5230,6 +5360,8 @@ mlxsw_sp_nexthop4_group_info_init(struct mlxsw_sp *mlxsw_sp, return 0; err_group_refresh: + mlxsw_sp_nexthop_group_dec(mlxsw_sp); +err_group_inc: i = nhgi->count; err_nexthop4_init: for (i--; i >= 0; i--) { @@ -5247,6 +5379,7 @@ mlxsw_sp_nexthop4_group_info_fini(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_nexthop_group_info *nhgi = nh_grp->nhgi; int i; + mlxsw_sp_nexthop_group_dec(mlxsw_sp); for (i = nhgi->count - 1; i >= 0; i--) { struct mlxsw_sp_nexthop *nh = &nhgi->nexthops[i]; @@ -5725,41 +5858,6 @@ static int mlxsw_sp_fib_entry_commit(struct mlxsw_sp *mlxsw_sp, return err; } -static int mlxsw_sp_adj_discard_write(struct mlxsw_sp *mlxsw_sp) -{ - enum mlxsw_reg_ratr_trap_action trap_action; - char ratr_pl[MLXSW_REG_RATR_LEN]; - int err; - - if (mlxsw_sp->router->adj_discard_index_valid) - return 0; - - err = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1, - &mlxsw_sp->router->adj_discard_index); - if (err) - return err; - - trap_action = MLXSW_REG_RATR_TRAP_ACTION_TRAP; - mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY, true, - MLXSW_REG_RATR_TYPE_ETHERNET, - mlxsw_sp->router->adj_discard_index, - mlxsw_sp->router->lb_rif_index); - mlxsw_reg_ratr_trap_action_set(ratr_pl, trap_action); - mlxsw_reg_ratr_trap_id_set(ratr_pl, MLXSW_TRAP_ID_RTR_EGRESS0); - err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl); - if (err) - goto err_ratr_write; - - mlxsw_sp->router->adj_discard_index_valid = true; - - return 0; - -err_ratr_write: - mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1, - mlxsw_sp->router->adj_discard_index); - return err; -} - static int mlxsw_sp_fib_entry_op_remote(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_entry_op_ctx *op_ctx, struct mlxsw_sp_fib_entry *fib_entry, @@ -5772,7 +5870,6 @@ static int mlxsw_sp_fib_entry_op_remote(struct mlxsw_sp *mlxsw_sp, u16 trap_id = 0; u32 adjacency_index = 0; u16 ecmp_size = 0; - int err; /* In case the nexthop group adjacency index is valid, use it * with provided ECMP size. Otherwise, setup trap and pass @@ -5783,11 +5880,8 @@ static int mlxsw_sp_fib_entry_op_remote(struct mlxsw_sp *mlxsw_sp, adjacency_index = nhgi->adj_index; ecmp_size = nhgi->ecmp_size; } else if (!nhgi->adj_index_valid && nhgi->count && nhgi->nh_rif) { - err = mlxsw_sp_adj_discard_write(mlxsw_sp); - if (err) - return err; trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP; - adjacency_index = mlxsw_sp->router->adj_discard_index; + adjacency_index = mlxsw_sp->router->adj_trap_index; ecmp_size = 1; } else { trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP; @@ -6036,8 +6130,8 @@ mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp, } static void -mlxsw_sp_fib4_entry_type_unset(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib_entry *fib_entry) +mlxsw_sp_fib_entry_type_unset(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry) { switch (fib_entry->type) { case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP: @@ -6048,6 +6142,13 @@ mlxsw_sp_fib4_entry_type_unset(struct mlxsw_sp *mlxsw_sp, } } +static void +mlxsw_sp_fib4_entry_type_unset(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib4_entry *fib4_entry) +{ + mlxsw_sp_fib_entry_type_unset(mlxsw_sp, &fib4_entry->common); +} + static struct mlxsw_sp_fib4_entry * mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_node *fib_node, @@ -6108,7 +6209,7 @@ static void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node; fib_info_put(fib4_entry->fi); - mlxsw_sp_fib4_entry_type_unset(mlxsw_sp, &fib4_entry->common); + mlxsw_sp_fib4_entry_type_unset(mlxsw_sp, fib4_entry); mlxsw_sp_nexthop_group_vr_unlink(fib4_entry->common.nh_group, fib_node->fib); mlxsw_sp_nexthop4_group_put(mlxsw_sp, &fib4_entry->common); @@ -6641,6 +6742,9 @@ mlxsw_sp_nexthop6_group_info_init(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_rt6 = list_next_entry(mlxsw_sp_rt6, list); } nh_grp->nhgi = nhgi; + err = mlxsw_sp_nexthop_group_inc(mlxsw_sp); + if (err) + goto err_group_inc; err = mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp); if (err) goto err_group_refresh; @@ -6648,6 +6752,8 @@ mlxsw_sp_nexthop6_group_info_init(struct mlxsw_sp *mlxsw_sp, return 0; err_group_refresh: + mlxsw_sp_nexthop_group_dec(mlxsw_sp); +err_group_inc: i = nhgi->count; err_nexthop6_init: for (i--; i >= 0; i--) { @@ -6665,6 +6771,7 @@ mlxsw_sp_nexthop6_group_info_fini(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_nexthop_group_info *nhgi = nh_grp->nhgi; int i; + mlxsw_sp_nexthop_group_dec(mlxsw_sp); for (i = nhgi->count - 1; i >= 0; i--) { struct mlxsw_sp_nexthop *nh = &nhgi->nexthops[i]; @@ -6888,11 +6995,38 @@ mlxsw_sp_fib6_entry_nexthop_del(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_nexthop6_group_update(mlxsw_sp, op_ctx, fib6_entry); } -static void mlxsw_sp_fib6_entry_type_set(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib_entry *fib_entry, - const struct fib6_info *rt) +static int +mlxsw_sp_fib6_entry_type_set_local(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry, + const struct fib6_info *rt) { - if (rt->fib6_flags & (RTF_LOCAL | RTF_ANYCAST)) + struct mlxsw_sp_nexthop_group_info *nhgi = fib_entry->nh_group->nhgi; + union mlxsw_sp_l3addr dip = { .addr6 = rt->fib6_dst.addr }; + int ifindex = nhgi->nexthops[0].ifindex; + struct mlxsw_sp_ipip_entry *ipip_entry; + + fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP; + ipip_entry = mlxsw_sp_ipip_entry_find_by_decap(mlxsw_sp, ifindex, + MLXSW_SP_L3_PROTO_IPV6, + dip); + + if (ipip_entry && ipip_entry->ol_dev->flags & IFF_UP) { + fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP; + return mlxsw_sp_fib_entry_decap_init(mlxsw_sp, fib_entry, + ipip_entry); + } + + return 0; +} + +static int mlxsw_sp_fib6_entry_type_set(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry, + const struct fib6_info *rt) +{ + if (rt->fib6_flags & RTF_LOCAL) + return mlxsw_sp_fib6_entry_type_set_local(mlxsw_sp, fib_entry, + rt); + if (rt->fib6_flags & RTF_ANYCAST) fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP; else if (rt->fib6_type == RTN_BLACKHOLE) fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_BLACKHOLE; @@ -6902,6 +7036,8 @@ static void mlxsw_sp_fib6_entry_type_set(struct mlxsw_sp *mlxsw_sp, fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE; else fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL; + + return 0; } static void @@ -6959,12 +7095,16 @@ mlxsw_sp_fib6_entry_create(struct mlxsw_sp *mlxsw_sp, if (err) goto err_nexthop_group_vr_link; - mlxsw_sp_fib6_entry_type_set(mlxsw_sp, fib_entry, rt_arr[0]); + err = mlxsw_sp_fib6_entry_type_set(mlxsw_sp, fib_entry, rt_arr[0]); + if (err) + goto err_fib6_entry_type_set; fib_entry->fib_node = fib_node; return fib6_entry; +err_fib6_entry_type_set: + mlxsw_sp_nexthop_group_vr_unlink(fib_entry->nh_group, fib_node->fib); err_nexthop_group_vr_link: mlxsw_sp_nexthop6_group_put(mlxsw_sp, fib_entry); err_nexthop6_group_get: @@ -6983,11 +7123,19 @@ err_fib_entry_priv_create: return ERR_PTR(err); } +static void +mlxsw_sp_fib6_entry_type_unset(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib6_entry *fib6_entry) +{ + mlxsw_sp_fib_entry_type_unset(mlxsw_sp, &fib6_entry->common); +} + static void mlxsw_sp_fib6_entry_destroy(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib6_entry *fib6_entry) { struct mlxsw_sp_fib_node *fib_node = fib6_entry->common.fib_node; + mlxsw_sp_fib6_entry_type_unset(mlxsw_sp, fib6_entry); mlxsw_sp_nexthop_group_vr_unlink(fib6_entry->common.nh_group, fib_node->fib); mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common); @@ -7340,16 +7488,6 @@ static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp) continue; mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV6); } - - /* After flushing all the routes, it is not possible anyone is still - * using the adjacency index that is discarding packets, so free it in - * case it was allocated. - */ - if (!mlxsw_sp->router->adj_discard_index_valid) - return; - mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1, - mlxsw_sp->router->adj_discard_index); - mlxsw_sp->router->adj_discard_index_valid = false; } struct mlxsw_sp_fib6_event { @@ -8056,7 +8194,7 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp, if (ops->setup) ops->setup(rif, params); - err = ops->configure(rif); + err = ops->configure(rif, extack); if (err) goto err_configure; @@ -8175,6 +8313,200 @@ static void mlxsw_sp_rif_subport_put(struct mlxsw_sp_rif *rif) mlxsw_sp_rif_destroy(rif); } +static int mlxsw_sp_rif_mac_profile_index_alloc(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_rif_mac_profile *profile, + struct netlink_ext_ack *extack) +{ + u8 max_rif_mac_profiles = mlxsw_sp->router->max_rif_mac_profile; + struct mlxsw_sp_router *router = mlxsw_sp->router; + int id; + + id = idr_alloc(&router->rif_mac_profiles_idr, profile, 0, + max_rif_mac_profiles, GFP_KERNEL); + + if (id >= 0) { + profile->id = id; + return 0; + } + + if (id == -ENOSPC) + NL_SET_ERR_MSG_MOD(extack, + "Exceeded number of supported router interface MAC profiles"); + + return id; +} + +static struct mlxsw_sp_rif_mac_profile * +mlxsw_sp_rif_mac_profile_index_free(struct mlxsw_sp *mlxsw_sp, u8 mac_profile) +{ + struct mlxsw_sp_rif_mac_profile *profile; + + profile = idr_remove(&mlxsw_sp->router->rif_mac_profiles_idr, + mac_profile); + WARN_ON(!profile); + return profile; +} + +static struct mlxsw_sp_rif_mac_profile * +mlxsw_sp_rif_mac_profile_alloc(const char *mac) +{ + struct mlxsw_sp_rif_mac_profile *profile; + + profile = kzalloc(sizeof(*profile), GFP_KERNEL); + if (!profile) + return NULL; + + ether_addr_copy(profile->mac_prefix, mac); + refcount_set(&profile->ref_count, 1); + return profile; +} + +static struct mlxsw_sp_rif_mac_profile * +mlxsw_sp_rif_mac_profile_find(const struct mlxsw_sp *mlxsw_sp, const char *mac) +{ + struct mlxsw_sp_router *router = mlxsw_sp->router; + struct mlxsw_sp_rif_mac_profile *profile; + int id; + + idr_for_each_entry(&router->rif_mac_profiles_idr, profile, id) { + if (!profile) + continue; + + if (ether_addr_equal_masked(profile->mac_prefix, mac, + mlxsw_sp->mac_mask)) + return profile; + } + + return NULL; +} + +static u64 mlxsw_sp_rif_mac_profiles_occ_get(void *priv) +{ + const struct mlxsw_sp *mlxsw_sp = priv; + + return atomic_read(&mlxsw_sp->router->rif_mac_profiles_count); +} + +static struct mlxsw_sp_rif_mac_profile * +mlxsw_sp_rif_mac_profile_create(struct mlxsw_sp *mlxsw_sp, const char *mac, + struct netlink_ext_ack *extack) +{ + struct mlxsw_sp_rif_mac_profile *profile; + int err; + + profile = mlxsw_sp_rif_mac_profile_alloc(mac); + if (!profile) + return ERR_PTR(-ENOMEM); + + err = mlxsw_sp_rif_mac_profile_index_alloc(mlxsw_sp, profile, extack); + if (err) + goto profile_index_alloc_err; + + atomic_inc(&mlxsw_sp->router->rif_mac_profiles_count); + return profile; + +profile_index_alloc_err: + kfree(profile); + return ERR_PTR(err); +} + +static void mlxsw_sp_rif_mac_profile_destroy(struct mlxsw_sp *mlxsw_sp, + u8 mac_profile) +{ + struct mlxsw_sp_rif_mac_profile *profile; + + atomic_dec(&mlxsw_sp->router->rif_mac_profiles_count); + profile = mlxsw_sp_rif_mac_profile_index_free(mlxsw_sp, mac_profile); + kfree(profile); +} + +static int mlxsw_sp_rif_mac_profile_get(struct mlxsw_sp *mlxsw_sp, + const char *mac, u8 *p_mac_profile, + struct netlink_ext_ack *extack) +{ + struct mlxsw_sp_rif_mac_profile *profile; + + profile = mlxsw_sp_rif_mac_profile_find(mlxsw_sp, mac); + if (profile) { + refcount_inc(&profile->ref_count); + goto out; + } + + profile = mlxsw_sp_rif_mac_profile_create(mlxsw_sp, mac, extack); + if (IS_ERR(profile)) + return PTR_ERR(profile); + +out: + *p_mac_profile = profile->id; + return 0; +} + +static void mlxsw_sp_rif_mac_profile_put(struct mlxsw_sp *mlxsw_sp, + u8 mac_profile) +{ + struct mlxsw_sp_rif_mac_profile *profile; + + profile = idr_find(&mlxsw_sp->router->rif_mac_profiles_idr, + mac_profile); + if (WARN_ON(!profile)) + return; + + if (!refcount_dec_and_test(&profile->ref_count)) + return; + + mlxsw_sp_rif_mac_profile_destroy(mlxsw_sp, mac_profile); +} + +static bool mlxsw_sp_rif_mac_profile_is_shared(const struct mlxsw_sp_rif *rif) +{ + struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; + struct mlxsw_sp_rif_mac_profile *profile; + + profile = idr_find(&mlxsw_sp->router->rif_mac_profiles_idr, + rif->mac_profile_id); + if (WARN_ON(!profile)) + return false; + + return refcount_read(&profile->ref_count) > 1; +} + +static int mlxsw_sp_rif_mac_profile_edit(struct mlxsw_sp_rif *rif, + const char *new_mac) +{ + struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; + struct mlxsw_sp_rif_mac_profile *profile; + + profile = idr_find(&mlxsw_sp->router->rif_mac_profiles_idr, + rif->mac_profile_id); + if (WARN_ON(!profile)) + return -EINVAL; + + ether_addr_copy(profile->mac_prefix, new_mac); + return 0; +} + +static int +mlxsw_sp_rif_mac_profile_replace(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_rif *rif, + const char *new_mac, + struct netlink_ext_ack *extack) +{ + u8 mac_profile; + int err; + + if (!mlxsw_sp_rif_mac_profile_is_shared(rif)) + return mlxsw_sp_rif_mac_profile_edit(rif, new_mac); + + err = mlxsw_sp_rif_mac_profile_get(mlxsw_sp, new_mac, + &mac_profile, extack); + if (err) + return err; + + mlxsw_sp_rif_mac_profile_put(mlxsw_sp, rif->mac_profile_id); + rif->mac_profile_id = mac_profile; + return 0; +} + static int __mlxsw_sp_port_vlan_router_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan, struct net_device *l3_dev, @@ -8523,36 +8855,6 @@ static int mlxsw_sp_inetaddr_macvlan_event(struct mlxsw_sp *mlxsw_sp, return 0; } -static int mlxsw_sp_router_port_check_rif_addr(struct mlxsw_sp *mlxsw_sp, - struct net_device *dev, - const unsigned char *dev_addr, - struct netlink_ext_ack *extack) -{ - struct mlxsw_sp_rif *rif; - int i; - - /* A RIF is not created for macvlan netdevs. Their MAC is used to - * populate the FDB - */ - if (netif_is_macvlan(dev) || netif_is_l3_master(dev)) - return 0; - - for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) { - rif = mlxsw_sp->router->rifs[i]; - if (rif && rif->ops && - rif->ops->type == MLXSW_SP_RIF_TYPE_IPIP_LB) - continue; - if (rif && rif->dev && rif->dev != dev && - !ether_addr_equal_masked(rif->dev->dev_addr, dev_addr, - mlxsw_sp->mac_mask)) { - NL_SET_ERR_MSG_MOD(extack, "All router interface MAC addresses must have the same prefix"); - return -EINVAL; - } - } - - return 0; -} - static int __mlxsw_sp_inetaddr_event(struct mlxsw_sp *mlxsw_sp, struct net_device *dev, unsigned long event, @@ -8618,11 +8920,6 @@ int mlxsw_sp_inetaddr_valid_event(struct notifier_block *unused, if (!mlxsw_sp_rif_should_config(rif, dev, event)) goto out; - err = mlxsw_sp_router_port_check_rif_addr(mlxsw_sp, dev, dev->dev_addr, - ivi->extack); - if (err) - goto out; - err = __mlxsw_sp_inetaddr_event(mlxsw_sp, dev, event, ivi->extack); out: mutex_unlock(&mlxsw_sp->router->lock); @@ -8706,11 +9003,6 @@ int mlxsw_sp_inet6addr_valid_event(struct notifier_block *unused, if (!mlxsw_sp_rif_should_config(rif, dev, event)) goto out; - err = mlxsw_sp_router_port_check_rif_addr(mlxsw_sp, dev, dev->dev_addr, - i6vi->extack); - if (err) - goto out; - err = __mlxsw_sp_inetaddr_event(mlxsw_sp, dev, event, i6vi->extack); out: mutex_unlock(&mlxsw_sp->router->lock); @@ -8718,7 +9010,7 @@ out: } static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index, - const char *mac, int mtu) + const char *mac, int mtu, u8 mac_profile) { char ritr_pl[MLXSW_REG_RITR_LEN]; int err; @@ -8730,15 +9022,18 @@ static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index, mlxsw_reg_ritr_mtu_set(ritr_pl, mtu); mlxsw_reg_ritr_if_mac_memcpy_to(ritr_pl, mac); + mlxsw_reg_ritr_if_mac_profile_id_set(ritr_pl, mac_profile); mlxsw_reg_ritr_op_set(ritr_pl, MLXSW_REG_RITR_RIF_CREATE); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); } static int mlxsw_sp_router_port_change_event(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_rif *rif) + struct mlxsw_sp_rif *rif, + struct netlink_ext_ack *extack) { struct net_device *dev = rif->dev; + u8 old_mac_profile; u16 fid_index; int err; @@ -8748,8 +9043,14 @@ mlxsw_sp_router_port_change_event(struct mlxsw_sp *mlxsw_sp, if (err) return err; + old_mac_profile = rif->mac_profile_id; + err = mlxsw_sp_rif_mac_profile_replace(mlxsw_sp, rif, dev->dev_addr, + extack); + if (err) + goto err_rif_mac_profile_replace; + err = mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, dev->dev_addr, - dev->mtu); + dev->mtu, rif->mac_profile_id); if (err) goto err_rif_edit; @@ -8779,8 +9080,11 @@ mlxsw_sp_router_port_change_event(struct mlxsw_sp *mlxsw_sp, return 0; err_rif_fdb_op: - mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, rif->addr, rif->mtu); + mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, rif->addr, rif->mtu, + old_mac_profile); err_rif_edit: + mlxsw_sp_rif_mac_profile_replace(mlxsw_sp, rif, rif->addr, extack); +err_rif_mac_profile_replace: mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, fid_index, true); return err; } @@ -8788,16 +9092,34 @@ err_rif_edit: static int mlxsw_sp_router_port_pre_changeaddr_event(struct mlxsw_sp_rif *rif, struct netdev_notifier_pre_changeaddr_info *info) { + struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; + struct mlxsw_sp_rif_mac_profile *profile; struct netlink_ext_ack *extack; + u8 max_rif_mac_profiles; + u64 occ; extack = netdev_notifier_info_to_extack(&info->info); - return mlxsw_sp_router_port_check_rif_addr(rif->mlxsw_sp, rif->dev, - info->dev_addr, extack); + + profile = mlxsw_sp_rif_mac_profile_find(mlxsw_sp, info->dev_addr); + if (profile) + return 0; + + max_rif_mac_profiles = mlxsw_sp->router->max_rif_mac_profile; + occ = mlxsw_sp_rif_mac_profiles_occ_get(mlxsw_sp); + if (occ < max_rif_mac_profiles) + return 0; + + if (!mlxsw_sp_rif_mac_profile_is_shared(rif)) + return 0; + + NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported router interface MAC profiles"); + return -ENOBUFS; } int mlxsw_sp_netdevice_router_port_event(struct net_device *dev, unsigned long event, void *ptr) { + struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(ptr); struct mlxsw_sp *mlxsw_sp; struct mlxsw_sp_rif *rif; int err = 0; @@ -8814,7 +9136,7 @@ int mlxsw_sp_netdevice_router_port_event(struct net_device *dev, switch (event) { case NETDEV_CHANGEMTU: case NETDEV_CHANGEADDR: - err = mlxsw_sp_router_port_change_event(mlxsw_sp, rif); + err = mlxsw_sp_router_port_change_event(mlxsw_sp, rif, extack); break; case NETDEV_PRE_CHANGEADDR: err = mlxsw_sp_router_port_pre_changeaddr_event(rif, ptr); @@ -8937,6 +9259,7 @@ static int mlxsw_sp_rif_subport_op(struct mlxsw_sp_rif *rif, bool enable) mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_SP_IF, rif->rif_index, rif->vr_id, rif->dev->mtu); mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr); + mlxsw_reg_ritr_if_mac_profile_id_set(ritr_pl, rif->mac_profile_id); mlxsw_reg_ritr_sp_if_pack(ritr_pl, rif_subport->lag, rif_subport->lag ? rif_subport->lag_id : rif_subport->system_port, @@ -8945,13 +9268,21 @@ static int mlxsw_sp_rif_subport_op(struct mlxsw_sp_rif *rif, bool enable) return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); } -static int mlxsw_sp_rif_subport_configure(struct mlxsw_sp_rif *rif) +static int mlxsw_sp_rif_subport_configure(struct mlxsw_sp_rif *rif, + struct netlink_ext_ack *extack) { + u8 mac_profile; int err; - err = mlxsw_sp_rif_subport_op(rif, true); + err = mlxsw_sp_rif_mac_profile_get(rif->mlxsw_sp, rif->addr, + &mac_profile, extack); if (err) return err; + rif->mac_profile_id = mac_profile; + + err = mlxsw_sp_rif_subport_op(rif, true); + if (err) + goto err_rif_subport_op; err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr, mlxsw_sp_fid_index(rif->fid), true); @@ -8963,6 +9294,8 @@ static int mlxsw_sp_rif_subport_configure(struct mlxsw_sp_rif *rif) err_rif_fdb_op: mlxsw_sp_rif_subport_op(rif, false); +err_rif_subport_op: + mlxsw_sp_rif_mac_profile_put(rif->mlxsw_sp, mac_profile); return err; } @@ -8975,6 +9308,7 @@ static void mlxsw_sp_rif_subport_deconfigure(struct mlxsw_sp_rif *rif) mlxsw_sp_fid_index(fid), false); mlxsw_sp_rif_macvlan_flush(rif); mlxsw_sp_rif_subport_op(rif, false); + mlxsw_sp_rif_mac_profile_put(rif->mlxsw_sp, rif->mac_profile_id); } static struct mlxsw_sp_fid * @@ -9003,6 +9337,7 @@ static int mlxsw_sp_rif_vlan_fid_op(struct mlxsw_sp_rif *rif, mlxsw_reg_ritr_pack(ritr_pl, enable, type, rif->rif_index, rif->vr_id, rif->dev->mtu); mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr); + mlxsw_reg_ritr_if_mac_profile_id_set(ritr_pl, rif->mac_profile_id); mlxsw_reg_ritr_fid_set(ritr_pl, type, vid_fid); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); @@ -9013,16 +9348,24 @@ u8 mlxsw_sp_router_port(const struct mlxsw_sp *mlxsw_sp) return mlxsw_core_max_ports(mlxsw_sp->core) + 1; } -static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif) +static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif, + struct netlink_ext_ack *extack) { struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; u16 fid_index = mlxsw_sp_fid_index(rif->fid); + u8 mac_profile; int err; + err = mlxsw_sp_rif_mac_profile_get(mlxsw_sp, rif->addr, + &mac_profile, extack); + if (err) + return err; + rif->mac_profile_id = mac_profile; + err = mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, true); if (err) - return err; + goto err_rif_vlan_fid_op; err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC, mlxsw_sp_router_port(mlxsw_sp), true); @@ -9050,6 +9393,8 @@ err_fid_bc_flood_set: mlxsw_sp_router_port(mlxsw_sp), false); err_fid_mc_flood_set: mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false); +err_rif_vlan_fid_op: + mlxsw_sp_rif_mac_profile_put(mlxsw_sp, mac_profile); return err; } @@ -9068,6 +9413,7 @@ static void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif) mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC, mlxsw_sp_router_port(mlxsw_sp), false); mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false); + mlxsw_sp_rif_mac_profile_put(rif->mlxsw_sp, rif->mac_profile_id); } static struct mlxsw_sp_fid * @@ -9172,7 +9518,8 @@ mlxsw_sp_rif_ipip_lb_setup(struct mlxsw_sp_rif *rif, } static int -mlxsw_sp1_rif_ipip_lb_configure(struct mlxsw_sp_rif *rif) +mlxsw_sp1_rif_ipip_lb_configure(struct mlxsw_sp_rif *rif, + struct netlink_ext_ack *extack) { struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif); u32 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(rif->dev); @@ -9359,7 +9706,8 @@ out: } static int -mlxsw_sp2_rif_ipip_lb_configure(struct mlxsw_sp_rif *rif) +mlxsw_sp2_rif_ipip_lb_configure(struct mlxsw_sp_rif *rif, + struct netlink_ext_ack *extack) { struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif); u32 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(rif->dev); @@ -9414,6 +9762,13 @@ static const struct mlxsw_sp_rif_ops *mlxsw_sp2_rif_ops_arr[] = { static int mlxsw_sp_rifs_init(struct mlxsw_sp *mlxsw_sp) { u64 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); + struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); + struct mlxsw_core *core = mlxsw_sp->core; + + if (!MLXSW_CORE_RES_VALID(core, MAX_RIF_MAC_PROFILES)) + return -EIO; + mlxsw_sp->router->max_rif_mac_profile = + MLXSW_CORE_RES_GET(core, MAX_RIF_MAC_PROFILES); mlxsw_sp->router->rifs = kcalloc(max_rifs, sizeof(struct mlxsw_sp_rif *), @@ -9421,16 +9776,28 @@ static int mlxsw_sp_rifs_init(struct mlxsw_sp *mlxsw_sp) if (!mlxsw_sp->router->rifs) return -ENOMEM; + idr_init(&mlxsw_sp->router->rif_mac_profiles_idr); + atomic_set(&mlxsw_sp->router->rif_mac_profiles_count, 0); + devlink_resource_occ_get_register(devlink, + MLXSW_SP_RESOURCE_RIF_MAC_PROFILES, + mlxsw_sp_rif_mac_profiles_occ_get, + mlxsw_sp); + return 0; } static void mlxsw_sp_rifs_fini(struct mlxsw_sp *mlxsw_sp) { + struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); int i; for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) WARN_ON_ONCE(mlxsw_sp->router->rifs[i]); + devlink_resource_occ_get_unregister(devlink, + MLXSW_SP_RESOURCE_RIF_MAC_PROFILES); + WARN_ON(!idr_is_empty(&mlxsw_sp->router->rif_mac_profiles_idr)); + idr_destroy(&mlxsw_sp->router->rif_mac_profiles_idr); kfree(mlxsw_sp->router->rifs); } @@ -9447,7 +9814,6 @@ static int mlxsw_sp_ipips_init(struct mlxsw_sp *mlxsw_sp) { int err; - mlxsw_sp->router->ipip_ops_arr = mlxsw_sp_ipip_ops_arr; INIT_LIST_HEAD(&mlxsw_sp->router->ipip_list); err = mlxsw_sp_ipip_ecn_encap_init(mlxsw_sp); @@ -9460,6 +9826,18 @@ static int mlxsw_sp_ipips_init(struct mlxsw_sp *mlxsw_sp) return mlxsw_sp_ipip_config_tigcr(mlxsw_sp); } +static int mlxsw_sp1_ipips_init(struct mlxsw_sp *mlxsw_sp) +{ + mlxsw_sp->router->ipip_ops_arr = mlxsw_sp1_ipip_ops_arr; + return mlxsw_sp_ipips_init(mlxsw_sp); +} + +static int mlxsw_sp2_ipips_init(struct mlxsw_sp *mlxsw_sp) +{ + mlxsw_sp->router->ipip_ops_arr = mlxsw_sp2_ipip_ops_arr; + return mlxsw_sp_ipips_init(mlxsw_sp); +} + static void mlxsw_sp_ipips_fini(struct mlxsw_sp *mlxsw_sp) { WARN_ON(!list_empty(&mlxsw_sp->router->ipip_list)); @@ -9874,6 +10252,7 @@ static int mlxsw_sp1_router_init(struct mlxsw_sp *mlxsw_sp) const struct mlxsw_sp_router_ops mlxsw_sp1_router_ops = { .init = mlxsw_sp1_router_init, + .ipips_init = mlxsw_sp1_ipips_init, }; static int mlxsw_sp2_router_init(struct mlxsw_sp *mlxsw_sp) @@ -9889,6 +10268,7 @@ static int mlxsw_sp2_router_init(struct mlxsw_sp *mlxsw_sp) const struct mlxsw_sp_router_ops mlxsw_sp2_router_ops = { .init = mlxsw_sp2_router_init, + .ipips_init = mlxsw_sp2_ipips_init, }; int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp, @@ -9934,7 +10314,7 @@ int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp, if (err) goto err_rifs_init; - err = mlxsw_sp_ipips_init(mlxsw_sp); + err = mlxsw_sp->router_ops->ipips_init(mlxsw_sp); if (err) goto err_ipips_init; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h index 25d3eae63501..99e8371a82a5 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h @@ -39,6 +39,9 @@ mlxsw_sp_fib_entry_op_ctx_clear(struct mlxsw_sp_fib_entry_op_ctx *op_ctx) struct mlxsw_sp_router { struct mlxsw_sp *mlxsw_sp; struct mlxsw_sp_rif **rifs; + struct idr rif_mac_profiles_idr; + atomic_t rif_mac_profiles_count; + u8 max_rif_mac_profile; struct mlxsw_sp_vr *vrs; struct rhashtable neigh_ht; struct rhashtable nexthop_group_ht; @@ -65,8 +68,6 @@ struct mlxsw_sp_router { struct notifier_block inet6addr_nb; const struct mlxsw_sp_rif_ops **rif_ops_arr; const struct mlxsw_sp_ipip_ops **ipip_ops_arr; - u32 adj_discard_index; - bool adj_discard_index_valid; struct mlxsw_sp_router_nve_decap nve_decap_config; struct mutex lock; /* Protects shared router resources */ struct work_struct fib_event_work; @@ -82,6 +83,8 @@ struct mlxsw_sp_router { struct delayed_work nh_grp_activity_dw; struct list_head nh_res_grp_list; bool inc_parsing_depth; + refcount_t num_groups; + u32 adj_trap_index; }; struct mlxsw_sp_fib_entry_priv { @@ -226,6 +229,8 @@ static inline bool mlxsw_sp_l3addr_eq(const union mlxsw_sp_l3addr *addr1, int mlxsw_sp_ipip_ecn_encap_init(struct mlxsw_sp *mlxsw_sp); int mlxsw_sp_ipip_ecn_decap_init(struct mlxsw_sp *mlxsw_sp); +struct net_device * +mlxsw_sp_ipip_netdev_ul_dev_get(const struct net_device *ol_dev); extern const struct mlxsw_sp_router_ll_ops mlxsw_sp_router_ll_xm_ops; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c index 3398cc01e5ec..f5f819aa9a65 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c @@ -1650,6 +1650,22 @@ void mlxsw_sp_span_trigger_disable(struct mlxsw_sp_port *mlxsw_sp_port, return trigger_entry->ops->disable(trigger_entry, mlxsw_sp_port, tc); } +bool mlxsw_sp_span_trigger_is_ingress(enum mlxsw_sp_span_trigger trigger) +{ + switch (trigger) { + case MLXSW_SP_SPAN_TRIGGER_INGRESS: + case MLXSW_SP_SPAN_TRIGGER_EARLY_DROP: + case MLXSW_SP_SPAN_TRIGGER_TAIL_DROP: + return true; + case MLXSW_SP_SPAN_TRIGGER_EGRESS: + case MLXSW_SP_SPAN_TRIGGER_ECN: + return false; + } + + WARN_ON_ONCE(1); + return false; +} + static int mlxsw_sp1_span_init(struct mlxsw_sp *mlxsw_sp) { size_t arr_size = ARRAY_SIZE(mlxsw_sp1_span_entry_ops_arr); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.h index efaefd1ae863..82e711afb02b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.h @@ -120,6 +120,7 @@ int mlxsw_sp_span_trigger_enable(struct mlxsw_sp_port *mlxsw_sp_port, enum mlxsw_sp_span_trigger trigger, u8 tc); void mlxsw_sp_span_trigger_disable(struct mlxsw_sp_port *mlxsw_sp_port, enum mlxsw_sp_span_trigger trigger, u8 tc); +bool mlxsw_sp_span_trigger_is_ingress(enum mlxsw_sp_span_trigger trigger); extern const struct mlxsw_sp_span_ops mlxsw_sp1_span_ops; extern const struct mlxsw_sp_span_ops mlxsw_sp2_span_ops; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 22fede5cb32c..81c7e8a7fcf5 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -1635,16 +1635,13 @@ mlxsw_sp_mid *__mlxsw_sp_mc_alloc(struct mlxsw_sp *mlxsw_sp, u16 fid) { struct mlxsw_sp_mid *mid; - size_t alloc_size; mid = kzalloc(sizeof(*mid), GFP_KERNEL); if (!mid) return NULL; - alloc_size = sizeof(unsigned long) * - BITS_TO_LONGS(mlxsw_core_max_ports(mlxsw_sp->core)); - - mid->ports_in_mid = kzalloc(alloc_size, GFP_KERNEL); + mid->ports_in_mid = bitmap_zalloc(mlxsw_core_max_ports(mlxsw_sp->core), + GFP_KERNEL); if (!mid->ports_in_mid) goto err_ports_in_mid_alloc; @@ -1663,7 +1660,7 @@ out: return mid; err_write_mdb_entry: - kfree(mid->ports_in_mid); + bitmap_free(mid->ports_in_mid); err_ports_in_mid_alloc: kfree(mid); return NULL; @@ -1680,7 +1677,7 @@ static int mlxsw_sp_port_remove_from_mid(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_core_max_ports(mlxsw_sp->core))) { err = mlxsw_sp_mc_remove_mdb_entry(mlxsw_sp, mid); list_del(&mid->list); - kfree(mid->ports_in_mid); + bitmap_free(mid->ports_in_mid); kfree(mid); } return err; |