diff options
-rw-r--r-- | Documentation/networking/devlink/mlxsw.rst | 24 | ||||
-rw-r--r-- | drivers/net/ethernet/mellanox/mlxsw/Kconfig | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/mellanox/mlxsw/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/mellanox/mlxsw/core.c | 44 | ||||
-rw-r--r-- | drivers/net/ethernet/mellanox/mlxsw/core.h | 35 | ||||
-rw-r--r-- | drivers/net/ethernet/mellanox/mlxsw/core_linecard_dev.c | 184 | ||||
-rw-r--r-- | drivers/net/ethernet/mellanox/mlxsw/core_linecards.c | 405 | ||||
-rw-r--r-- | drivers/net/ethernet/mellanox/mlxsw/reg.h | 173 | ||||
-rw-r--r-- | include/net/devlink.h | 2 | ||||
-rw-r--r-- | include/uapi/linux/devlink.h | 2 | ||||
-rw-r--r-- | net/core/devlink.c | 288 | ||||
-rwxr-xr-x | tools/testing/selftests/drivers/net/mlxsw/devlink_linecard.sh | 54 |
12 files changed, 1043 insertions, 171 deletions
diff --git a/Documentation/networking/devlink/mlxsw.rst b/Documentation/networking/devlink/mlxsw.rst index cf857cb4ba8f..433962225bd4 100644 --- a/Documentation/networking/devlink/mlxsw.rst +++ b/Documentation/networking/devlink/mlxsw.rst @@ -58,6 +58,30 @@ The ``mlxsw`` driver reports the following versions - running - Three digit firmware version +Line card auxiliary device info versions +======================================== + +The ``mlxsw`` driver reports the following versions for line card auxiliary device + +.. list-table:: devlink info versions implemented + :widths: 5 5 90 + + * - Name + - Type + - Description + * - ``hw.revision`` + - fixed + - The hardware revision for this line card + * - ``ini.version`` + - running + - Version of line card INI loaded + * - ``fw.psid`` + - fixed + - Line card device PSID + * - ``fw.version`` + - running + - Three digit firmware version of line card device + Driver-specific Traps ===================== diff --git a/drivers/net/ethernet/mellanox/mlxsw/Kconfig b/drivers/net/ethernet/mellanox/mlxsw/Kconfig index 4683312861ac..a510bf2cff2f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/Kconfig +++ b/drivers/net/ethernet/mellanox/mlxsw/Kconfig @@ -7,6 +7,7 @@ config MLXSW_CORE tristate "Mellanox Technologies Switch ASICs support" select NET_DEVLINK select MLXFW + select AUXILIARY_BUS help This driver supports Mellanox Technologies Switch ASICs family. diff --git a/drivers/net/ethernet/mellanox/mlxsw/Makefile b/drivers/net/ethernet/mellanox/mlxsw/Makefile index c2d6d64ffe4b..3ca9fce759ea 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/Makefile +++ b/drivers/net/ethernet/mellanox/mlxsw/Makefile @@ -2,7 +2,7 @@ obj-$(CONFIG_MLXSW_CORE) += mlxsw_core.o mlxsw_core-objs := core.o core_acl_flex_keys.o \ core_acl_flex_actions.o core_env.o \ - core_linecards.o + core_linecards.o core_linecard_dev.o mlxsw_core-$(CONFIG_MLXSW_CORE_HWMON) += core_hwmon.o mlxsw_core-$(CONFIG_MLXSW_CORE_THERMAL) += core_thermal.o obj-$(CONFIG_MLXSW_PCI) += mlxsw_pci.o diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index 1b61bc8f59a2..a48f893cf7b0 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -951,6 +951,20 @@ static struct mlxsw_driver *mlxsw_core_driver_get(const char *kind) return mlxsw_driver; } +int mlxsw_core_fw_flash(struct mlxsw_core *mlxsw_core, + struct mlxfw_dev *mlxfw_dev, + const struct firmware *firmware, + struct netlink_ext_ack *extack) +{ + int err; + + mlxsw_core->fw_flash_in_progress = true; + err = mlxfw_firmware_flash(mlxfw_dev, firmware, extack); + mlxsw_core->fw_flash_in_progress = false; + + return err; +} + struct mlxsw_core_fw_info { struct mlxfw_dev mlxfw_dev; struct mlxsw_core *mlxsw_core; @@ -1105,8 +1119,9 @@ static const struct mlxfw_dev_ops mlxsw_core_fw_mlxsw_dev_ops = { .fsm_release = mlxsw_core_fw_fsm_release, }; -static int mlxsw_core_fw_flash(struct mlxsw_core *mlxsw_core, const struct firmware *firmware, - struct netlink_ext_ack *extack) +static int mlxsw_core_dev_fw_flash(struct mlxsw_core *mlxsw_core, + const struct firmware *firmware, + struct netlink_ext_ack *extack) { struct mlxsw_core_fw_info mlxsw_core_fw_info = { .mlxfw_dev = { @@ -1117,13 +1132,9 @@ static int mlxsw_core_fw_flash(struct mlxsw_core *mlxsw_core, const struct firmw }, .mlxsw_core = mlxsw_core }; - int err; - - mlxsw_core->fw_flash_in_progress = true; - err = mlxfw_firmware_flash(&mlxsw_core_fw_info.mlxfw_dev, firmware, extack); - mlxsw_core->fw_flash_in_progress = false; - return err; + return mlxsw_core_fw_flash(mlxsw_core, &mlxsw_core_fw_info.mlxfw_dev, + firmware, extack); } static int mlxsw_core_fw_rev_validate(struct mlxsw_core *mlxsw_core, @@ -1169,7 +1180,7 @@ static int mlxsw_core_fw_rev_validate(struct mlxsw_core *mlxsw_core, return err; } - err = mlxsw_core_fw_flash(mlxsw_core, firmware, NULL); + err = mlxsw_core_dev_fw_flash(mlxsw_core, firmware, NULL); release_firmware(firmware); if (err) dev_err(mlxsw_bus_info->dev, "Could not upgrade firmware\n"); @@ -1187,7 +1198,7 @@ static int mlxsw_core_fw_flash_update(struct mlxsw_core *mlxsw_core, struct devlink_flash_update_params *params, struct netlink_ext_ack *extack) { - return mlxsw_core_fw_flash(mlxsw_core, params->fw, extack); + return mlxsw_core_dev_fw_flash(mlxsw_core, params->fw, extack); } static int mlxsw_core_devlink_param_fw_load_policy_validate(struct devlink *devlink, u32 id, @@ -3334,9 +3345,15 @@ static int __init mlxsw_core_module_init(void) { int err; + err = mlxsw_linecard_driver_register(); + if (err) + return err; + mlxsw_wq = alloc_workqueue(mlxsw_core_driver_name, 0, 0); - if (!mlxsw_wq) - return -ENOMEM; + if (!mlxsw_wq) { + err = -ENOMEM; + goto err_alloc_workqueue; + } mlxsw_owq = alloc_ordered_workqueue("%s_ordered", 0, mlxsw_core_driver_name); if (!mlxsw_owq) { @@ -3347,6 +3364,8 @@ static int __init mlxsw_core_module_init(void) err_alloc_ordered_workqueue: destroy_workqueue(mlxsw_wq); +err_alloc_workqueue: + mlxsw_linecard_driver_unregister(); return err; } @@ -3354,6 +3373,7 @@ static void __exit mlxsw_core_module_exit(void) { destroy_workqueue(mlxsw_owq); destroy_workqueue(mlxsw_wq); + mlxsw_linecard_driver_unregister(); } module_init(mlxsw_core_module_init); diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index 9d2e8a8d3a75..7213e4528298 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h @@ -12,12 +12,14 @@ #include <linux/skbuff.h> #include <linux/workqueue.h> #include <linux/net_namespace.h> +#include <linux/auxiliary_bus.h> #include <net/devlink.h> #include "trap.h" #include "reg.h" #include "cmd.h" #include "resources.h" +#include "../mlxfw/mlxfw.h" enum mlxsw_core_resource_id { MLXSW_CORE_RESOURCE_PORTS = 1, @@ -47,6 +49,11 @@ mlxsw_core_fw_rev_minor_subminor_validate(const struct mlxsw_fw_rev *rev, int mlxsw_core_driver_register(struct mlxsw_driver *mlxsw_driver); void mlxsw_core_driver_unregister(struct mlxsw_driver *mlxsw_driver); +int mlxsw_core_fw_flash(struct mlxsw_core *mlxsw_core, + struct mlxfw_dev *mlxfw_dev, + const struct firmware *firmware, + struct netlink_ext_ack *extack); + int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, const struct mlxsw_bus *mlxsw_bus, void *bus_priv, bool reload, @@ -563,6 +570,15 @@ enum mlxsw_linecard_status_event_type { MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION, }; +struct mlxsw_linecard_bdev; + +struct mlxsw_linecard_device_info { + u16 fw_major; + u16 fw_minor; + u16 fw_sub_minor; + char psid[MLXSW_REG_MGIR_FW_INFO_PSID_SIZE]; +}; + struct mlxsw_linecard { u8 slot_index; struct mlxsw_linecards *linecards; @@ -577,6 +593,11 @@ struct mlxsw_linecard { active:1; u16 hw_revision; u16 ini_version; + struct mlxsw_linecard_bdev *bdev; + struct { + struct mlxsw_linecard_device_info info; + u8 index; + } device; }; struct mlxsw_linecard_types_info; @@ -597,6 +618,14 @@ mlxsw_linecard_get(struct mlxsw_linecards *linecards, u8 slot_index) return &linecards->linecards[slot_index - 1]; } +int mlxsw_linecard_devlink_info_get(struct mlxsw_linecard *linecard, + struct devlink_info_req *req, + struct netlink_ext_ack *extack); +int mlxsw_linecard_flash_update(struct devlink *linecard_devlink, + struct mlxsw_linecard *linecard, + const struct firmware *firmware, + struct netlink_ext_ack *extack); + int mlxsw_linecards_init(struct mlxsw_core *mlxsw_core, const struct mlxsw_bus_info *bus_info); void mlxsw_linecards_fini(struct mlxsw_core *mlxsw_core); @@ -616,4 +645,10 @@ void mlxsw_linecards_event_ops_unregister(struct mlxsw_core *mlxsw_core, struct mlxsw_linecards_event_ops *ops, void *priv); +int mlxsw_linecard_bdev_add(struct mlxsw_linecard *linecard); +void mlxsw_linecard_bdev_del(struct mlxsw_linecard *linecard); + +int mlxsw_linecard_driver_register(void); +void mlxsw_linecard_driver_unregister(void); + #endif diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_linecard_dev.c b/drivers/net/ethernet/mellanox/mlxsw/core_linecard_dev.c new file mode 100644 index 000000000000..49fee038a99c --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlxsw/core_linecard_dev.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* Copyright (c) 2022 NVIDIA Corporation and Mellanox Technologies. All rights reserved */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/types.h> +#include <linux/err.h> +#include <linux/auxiliary_bus.h> +#include <linux/idr.h> +#include <linux/gfp.h> +#include <linux/slab.h> +#include <net/devlink.h> +#include "core.h" + +#define MLXSW_LINECARD_DEV_ID_NAME "lc" + +struct mlxsw_linecard_dev { + struct mlxsw_linecard *linecard; +}; + +struct mlxsw_linecard_bdev { + struct auxiliary_device adev; + struct mlxsw_linecard *linecard; + struct mlxsw_linecard_dev *linecard_dev; +}; + +static DEFINE_IDA(mlxsw_linecard_bdev_ida); + +static int mlxsw_linecard_bdev_id_alloc(void) +{ + return ida_alloc(&mlxsw_linecard_bdev_ida, GFP_KERNEL); +} + +static void mlxsw_linecard_bdev_id_free(int id) +{ + ida_free(&mlxsw_linecard_bdev_ida, id); +} + +static void mlxsw_linecard_bdev_release(struct device *device) +{ + struct auxiliary_device *adev = + container_of(device, struct auxiliary_device, dev); + struct mlxsw_linecard_bdev *linecard_bdev = + container_of(adev, struct mlxsw_linecard_bdev, adev); + + mlxsw_linecard_bdev_id_free(adev->id); + kfree(linecard_bdev); +} + +int mlxsw_linecard_bdev_add(struct mlxsw_linecard *linecard) +{ + struct mlxsw_linecard_bdev *linecard_bdev; + int err; + int id; + + id = mlxsw_linecard_bdev_id_alloc(); + if (id < 0) + return id; + + linecard_bdev = kzalloc(sizeof(*linecard_bdev), GFP_KERNEL); + if (!linecard_bdev) { + mlxsw_linecard_bdev_id_free(id); + return -ENOMEM; + } + linecard_bdev->adev.id = id; + linecard_bdev->adev.name = MLXSW_LINECARD_DEV_ID_NAME; + linecard_bdev->adev.dev.release = mlxsw_linecard_bdev_release; + linecard_bdev->adev.dev.parent = linecard->linecards->bus_info->dev; + linecard_bdev->linecard = linecard; + + err = auxiliary_device_init(&linecard_bdev->adev); + if (err) { + mlxsw_linecard_bdev_id_free(id); + kfree(linecard_bdev); + return err; + } + + err = auxiliary_device_add(&linecard_bdev->adev); + if (err) { + auxiliary_device_uninit(&linecard_bdev->adev); + return err; + } + + linecard->bdev = linecard_bdev; + return 0; +} + +void mlxsw_linecard_bdev_del(struct mlxsw_linecard *linecard) +{ + struct mlxsw_linecard_bdev *linecard_bdev = linecard->bdev; + + if (!linecard_bdev) + /* Unprovisioned line cards do not have an auxiliary device. */ + return; + auxiliary_device_delete(&linecard_bdev->adev); + auxiliary_device_uninit(&linecard_bdev->adev); + linecard->bdev = NULL; +} + +static int mlxsw_linecard_dev_devlink_info_get(struct devlink *devlink, + struct devlink_info_req *req, + struct netlink_ext_ack *extack) +{ + struct mlxsw_linecard_dev *linecard_dev = devlink_priv(devlink); + struct mlxsw_linecard *linecard = linecard_dev->linecard; + + return mlxsw_linecard_devlink_info_get(linecard, req, extack); +} + +static int +mlxsw_linecard_dev_devlink_flash_update(struct devlink *devlink, + struct devlink_flash_update_params *params, + struct netlink_ext_ack *extack) +{ + struct mlxsw_linecard_dev *linecard_dev = devlink_priv(devlink); + struct mlxsw_linecard *linecard = linecard_dev->linecard; + + return mlxsw_linecard_flash_update(devlink, linecard, + params->fw, extack); +} + +static const struct devlink_ops mlxsw_linecard_dev_devlink_ops = { + .info_get = mlxsw_linecard_dev_devlink_info_get, + .flash_update = mlxsw_linecard_dev_devlink_flash_update, +}; + +static int mlxsw_linecard_bdev_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct mlxsw_linecard_bdev *linecard_bdev = + container_of(adev, struct mlxsw_linecard_bdev, adev); + struct mlxsw_linecard *linecard = linecard_bdev->linecard; + struct mlxsw_linecard_dev *linecard_dev; + struct devlink *devlink; + + devlink = devlink_alloc(&mlxsw_linecard_dev_devlink_ops, + sizeof(*linecard_dev), &adev->dev); + if (!devlink) + return -ENOMEM; + linecard_dev = devlink_priv(devlink); + linecard_dev->linecard = linecard_bdev->linecard; + linecard_bdev->linecard_dev = linecard_dev; + + devlink_register(devlink); + devlink_linecard_nested_dl_set(linecard->devlink_linecard, devlink); + return 0; +} + +static void mlxsw_linecard_bdev_remove(struct auxiliary_device *adev) +{ + struct mlxsw_linecard_bdev *linecard_bdev = + container_of(adev, struct mlxsw_linecard_bdev, adev); + struct devlink *devlink = priv_to_devlink(linecard_bdev->linecard_dev); + struct mlxsw_linecard *linecard = linecard_bdev->linecard; + + devlink_linecard_nested_dl_set(linecard->devlink_linecard, NULL); + devlink_unregister(devlink); + devlink_free(devlink); +} + +static const struct auxiliary_device_id mlxsw_linecard_bdev_id_table[] = { + { .name = KBUILD_MODNAME "." MLXSW_LINECARD_DEV_ID_NAME }, + {}, +}; + +MODULE_DEVICE_TABLE(auxiliary, mlxsw_linecard_bdev_id_table); + +static struct auxiliary_driver mlxsw_linecard_driver = { + .name = MLXSW_LINECARD_DEV_ID_NAME, + .probe = mlxsw_linecard_bdev_probe, + .remove = mlxsw_linecard_bdev_remove, + .id_table = mlxsw_linecard_bdev_id_table, +}; + +int mlxsw_linecard_driver_register(void) +{ + return auxiliary_driver_register(&mlxsw_linecard_driver); +} + +void mlxsw_linecard_driver_unregister(void) +{ + auxiliary_driver_unregister(&mlxsw_linecard_driver); +} diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_linecards.c b/drivers/net/ethernet/mellanox/mlxsw/core_linecards.c index 5c9869dcf674..ca59f0b946da 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_linecards.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_linecards.c @@ -13,6 +13,7 @@ #include <linux/vmalloc.h> #include "core.h" +#include "../mlxfw/mlxfw.h" struct mlxsw_linecard_ini_file { __le16 size; @@ -87,6 +88,351 @@ static const char *mlxsw_linecard_type_name(struct mlxsw_linecard *linecard) return linecard->name; } +struct mlxsw_linecard_device_fw_info { + struct mlxfw_dev mlxfw_dev; + struct mlxsw_core *mlxsw_core; + struct mlxsw_linecard *linecard; +}; + +static int mlxsw_linecard_device_fw_component_query(struct mlxfw_dev *mlxfw_dev, + u16 component_index, + u32 *p_max_size, + u8 *p_align_bits, + u16 *p_max_write_size) +{ + struct mlxsw_linecard_device_fw_info *info = + container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info, + mlxfw_dev); + struct mlxsw_linecard *linecard = info->linecard; + struct mlxsw_core *mlxsw_core = info->mlxsw_core; + char mddt_pl[MLXSW_REG_MDDT_LEN]; + char *mcqi_pl; + int err; + + mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, + linecard->device.index, + MLXSW_REG_MDDT_METHOD_QUERY, + MLXSW_REG(mcqi), &mcqi_pl); + + mlxsw_reg_mcqi_pack(mcqi_pl, component_index); + err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl); + if (err) + return err; + mlxsw_reg_mcqi_unpack(mcqi_pl, p_max_size, p_align_bits, + p_max_write_size); + + *p_align_bits = max_t(u8, *p_align_bits, 2); + *p_max_write_size = min_t(u16, *p_max_write_size, + MLXSW_REG_MCDA_MAX_DATA_LEN); + return 0; +} + +static int mlxsw_linecard_device_fw_fsm_lock(struct mlxfw_dev *mlxfw_dev, + u32 *fwhandle) +{ + struct mlxsw_linecard_device_fw_info *info = + container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info, + mlxfw_dev); + struct mlxsw_linecard *linecard = info->linecard; + struct mlxsw_core *mlxsw_core = info->mlxsw_core; + char mddt_pl[MLXSW_REG_MDDT_LEN]; + u8 control_state; + char *mcc_pl; + int err; + + mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, + linecard->device.index, + MLXSW_REG_MDDT_METHOD_QUERY, + MLXSW_REG(mcc), &mcc_pl); + mlxsw_reg_mcc_pack(mcc_pl, 0, 0, 0, 0); + err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl); + if (err) + return err; + + mlxsw_reg_mcc_unpack(mcc_pl, fwhandle, NULL, &control_state); + if (control_state != MLXFW_FSM_STATE_IDLE) + return -EBUSY; + + mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, + linecard->device.index, + MLXSW_REG_MDDT_METHOD_WRITE, + MLXSW_REG(mcc), &mcc_pl); + mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_LOCK_UPDATE_HANDLE, + 0, *fwhandle, 0); + return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl); +} + +static int +mlxsw_linecard_device_fw_fsm_component_update(struct mlxfw_dev *mlxfw_dev, + u32 fwhandle, + u16 component_index, + u32 component_size) +{ + struct mlxsw_linecard_device_fw_info *info = + container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info, + mlxfw_dev); + struct mlxsw_linecard *linecard = info->linecard; + struct mlxsw_core *mlxsw_core = info->mlxsw_core; + char mddt_pl[MLXSW_REG_MDDT_LEN]; + char *mcc_pl; + + mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, + linecard->device.index, + MLXSW_REG_MDDT_METHOD_WRITE, + MLXSW_REG(mcc), &mcc_pl); + mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_UPDATE_COMPONENT, + component_index, fwhandle, component_size); + return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl); +} + +static int +mlxsw_linecard_device_fw_fsm_block_download(struct mlxfw_dev *mlxfw_dev, + u32 fwhandle, u8 *data, + u16 size, u32 offset) +{ + struct mlxsw_linecard_device_fw_info *info = + container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info, + mlxfw_dev); + struct mlxsw_linecard *linecard = info->linecard; + struct mlxsw_core *mlxsw_core = info->mlxsw_core; + char mddt_pl[MLXSW_REG_MDDT_LEN]; + char *mcda_pl; + + mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, + linecard->device.index, + MLXSW_REG_MDDT_METHOD_WRITE, + MLXSW_REG(mcda), &mcda_pl); + mlxsw_reg_mcda_pack(mcda_pl, fwhandle, offset, size, data); + return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl); +} + +static int +mlxsw_linecard_device_fw_fsm_component_verify(struct mlxfw_dev *mlxfw_dev, + u32 fwhandle, u16 component_index) +{ + struct mlxsw_linecard_device_fw_info *info = + container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info, + mlxfw_dev); + struct mlxsw_linecard *linecard = info->linecard; + struct mlxsw_core *mlxsw_core = info->mlxsw_core; + char mddt_pl[MLXSW_REG_MDDT_LEN]; + char *mcc_pl; + + mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, + linecard->device.index, + MLXSW_REG_MDDT_METHOD_WRITE, + MLXSW_REG(mcc), &mcc_pl); + mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_VERIFY_COMPONENT, + component_index, fwhandle, 0); + return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl); +} + +static int mlxsw_linecard_device_fw_fsm_activate(struct mlxfw_dev *mlxfw_dev, + u32 fwhandle) +{ + struct mlxsw_linecard_device_fw_info *info = + container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info, + mlxfw_dev); + struct mlxsw_linecard *linecard = info->linecard; + struct mlxsw_core *mlxsw_core = info->mlxsw_core; + char mddt_pl[MLXSW_REG_MDDT_LEN]; + char *mcc_pl; + + mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, + linecard->device.index, + MLXSW_REG_MDDT_METHOD_WRITE, + MLXSW_REG(mcc), &mcc_pl); + mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_ACTIVATE, + 0, fwhandle, 0); + return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl); +} + +static int +mlxsw_linecard_device_fw_fsm_query_state(struct mlxfw_dev *mlxfw_dev, + u32 fwhandle, + enum mlxfw_fsm_state *fsm_state, + enum mlxfw_fsm_state_err *fsm_state_err) +{ + struct mlxsw_linecard_device_fw_info *info = + container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info, + mlxfw_dev); + struct mlxsw_linecard *linecard = info->linecard; + struct mlxsw_core *mlxsw_core = info->mlxsw_core; + char mddt_pl[MLXSW_REG_MDDT_LEN]; + u8 control_state; + u8 error_code; + char *mcc_pl; + int err; + + mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, + linecard->device.index, + MLXSW_REG_MDDT_METHOD_QUERY, + MLXSW_REG(mcc), &mcc_pl); + mlxsw_reg_mcc_pack(mcc_pl, 0, 0, fwhandle, 0); + err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl); + if (err) + return err; + + mlxsw_reg_mcc_unpack(mcc_pl, NULL, &error_code, &control_state); + *fsm_state = control_state; + *fsm_state_err = min_t(enum mlxfw_fsm_state_err, error_code, + MLXFW_FSM_STATE_ERR_MAX); + return 0; +} + +static void mlxsw_linecard_device_fw_fsm_cancel(struct mlxfw_dev *mlxfw_dev, + u32 fwhandle) +{ + struct mlxsw_linecard_device_fw_info *info = + container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info, + mlxfw_dev); + struct mlxsw_linecard *linecard = info->linecard; + struct mlxsw_core *mlxsw_core = info->mlxsw_core; + char mddt_pl[MLXSW_REG_MDDT_LEN]; + char *mcc_pl; + + mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, + linecard->device.index, + MLXSW_REG_MDDT_METHOD_WRITE, + MLXSW_REG(mcc), &mcc_pl); + mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_CANCEL, + 0, fwhandle, 0); + mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl); +} + +static void mlxsw_linecard_device_fw_fsm_release(struct mlxfw_dev *mlxfw_dev, + u32 fwhandle) +{ + struct mlxsw_linecard_device_fw_info *info = + container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info, + mlxfw_dev); + struct mlxsw_linecard *linecard = info->linecard; + struct mlxsw_core *mlxsw_core = info->mlxsw_core; + char mddt_pl[MLXSW_REG_MDDT_LEN]; + char *mcc_pl; + + mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, + linecard->device.index, + MLXSW_REG_MDDT_METHOD_WRITE, + MLXSW_REG(mcc), &mcc_pl); + mlxsw_reg_mcc_pack(mcc_pl, + MLXSW_REG_MCC_INSTRUCTION_RELEASE_UPDATE_HANDLE, + 0, fwhandle, 0); + mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl); +} + +static const struct mlxfw_dev_ops mlxsw_linecard_device_dev_ops = { + .component_query = mlxsw_linecard_device_fw_component_query, + .fsm_lock = mlxsw_linecard_device_fw_fsm_lock, + .fsm_component_update = mlxsw_linecard_device_fw_fsm_component_update, + .fsm_block_download = mlxsw_linecard_device_fw_fsm_block_download, + .fsm_component_verify = mlxsw_linecard_device_fw_fsm_component_verify, + .fsm_activate = mlxsw_linecard_device_fw_fsm_activate, + .fsm_query_state = mlxsw_linecard_device_fw_fsm_query_state, + .fsm_cancel = mlxsw_linecard_device_fw_fsm_cancel, + .fsm_release = mlxsw_linecard_device_fw_fsm_release, +}; + +int mlxsw_linecard_flash_update(struct devlink *linecard_devlink, + struct mlxsw_linecard *linecard, + const struct firmware *firmware, + struct netlink_ext_ack *extack) +{ + struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core; + struct mlxsw_linecard_device_fw_info info = { + .mlxfw_dev = { + .ops = &mlxsw_linecard_device_dev_ops, + .psid = linecard->device.info.psid, + .psid_size = strlen(linecard->device.info.psid), + .devlink = linecard_devlink, + }, + .mlxsw_core = mlxsw_core, + .linecard = linecard, + }; + int err; + + mutex_lock(&linecard->lock); + if (!linecard->active) { + NL_SET_ERR_MSG_MOD(extack, "Only active line cards can be flashed"); + err = -EINVAL; + goto unlock; + } + err = mlxsw_core_fw_flash(mlxsw_core, &info.mlxfw_dev, + firmware, extack); +unlock: + mutex_unlock(&linecard->lock); + return err; +} + +static int mlxsw_linecard_device_psid_get(struct mlxsw_linecard *linecard, + u8 device_index, char *psid) +{ + struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core; + char mddt_pl[MLXSW_REG_MDDT_LEN]; + char *mgir_pl; + int err; + + mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, device_index, + MLXSW_REG_MDDT_METHOD_QUERY, + MLXSW_REG(mgir), &mgir_pl); + + mlxsw_reg_mgir_pack(mgir_pl); + err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl); + if (err) + return err; + + mlxsw_reg_mgir_fw_info_psid_memcpy_from(mgir_pl, psid); + return 0; +} + +static int mlxsw_linecard_device_info_update(struct mlxsw_linecard *linecard) +{ + struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core; + bool flashable_found = false; + u8 msg_seq = 0; + + do { + struct mlxsw_linecard_device_info info; + char mddq_pl[MLXSW_REG_MDDQ_LEN]; + bool flash_owner; + bool data_valid; + u8 device_index; + int err; + + mlxsw_reg_mddq_device_info_pack(mddq_pl, linecard->slot_index, + msg_seq); + err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl); + if (err) + return err; + mlxsw_reg_mddq_device_info_unpack(mddq_pl, &msg_seq, + &data_valid, &flash_owner, + &device_index, + &info.fw_major, + &info.fw_minor, + &info.fw_sub_minor); + if (!data_valid) + break; + if (!flash_owner) /* We care only about flashable ones. */ + continue; + if (flashable_found) { + dev_warn_once(linecard->linecards->bus_info->dev, "linecard %u: More flashable devices present, exposing only the first one\n", + linecard->slot_index); + return 0; + } + + err = mlxsw_linecard_device_psid_get(linecard, device_index, + info.psid); + if (err) + return err; + + linecard->device.info = info; + linecard->device.index = device_index; + flashable_found = true; + } while (msg_seq); + + return 0; +} + static void mlxsw_linecard_provision_fail(struct mlxsw_linecard *linecard) { linecard->provisioned = false; @@ -226,12 +572,57 @@ void mlxsw_linecards_event_ops_unregister(struct mlxsw_core *mlxsw_core, } EXPORT_SYMBOL(mlxsw_linecards_event_ops_unregister); +int mlxsw_linecard_devlink_info_get(struct mlxsw_linecard *linecard, + struct devlink_info_req *req, + struct netlink_ext_ack *extack) +{ + char buf[32]; + int err; + + mutex_lock(&linecard->lock); + if (WARN_ON(!linecard->provisioned)) { + err = -EOPNOTSUPP; + goto unlock; + } + + sprintf(buf, "%d", linecard->hw_revision); + err = devlink_info_version_fixed_put(req, "hw.revision", buf); + if (err) + goto unlock; + + sprintf(buf, "%d", linecard->ini_version); + err = devlink_info_version_running_put(req, "ini.version", buf); + if (err) + goto unlock; + + if (linecard->active) { + struct mlxsw_linecard_device_info *info = &linecard->device.info; + + err = devlink_info_version_fixed_put(req, + DEVLINK_INFO_VERSION_GENERIC_FW_PSID, + info->psid); + + sprintf(buf, "%u.%u.%u", info->fw_major, info->fw_minor, + info->fw_sub_minor); + err = devlink_info_version_running_put(req, + DEVLINK_INFO_VERSION_GENERIC_FW, + buf); + if (err) + goto unlock; + } + +unlock: + mutex_unlock(&linecard->lock); + return err; +} + static int mlxsw_linecard_provision_set(struct mlxsw_linecard *linecard, u8 card_type, u16 hw_revision, u16 ini_version) { struct mlxsw_linecards *linecards = linecard->linecards; const char *type; + int err; type = mlxsw_linecard_types_lookup(linecards, card_type); mlxsw_linecard_status_event_done(linecard, @@ -252,6 +643,14 @@ mlxsw_linecard_provision_set(struct mlxsw_linecard *linecard, u8 card_type, linecard->provisioned = true; linecard->hw_revision = hw_revision; linecard->ini_version = ini_version; + + err = mlxsw_linecard_bdev_add(linecard); + if (err) { + linecard->provisioned = false; + mlxsw_linecard_provision_fail(linecard); + return err; + } + devlink_linecard_provision_set(linecard->devlink_linecard, type); return 0; } @@ -260,6 +659,7 @@ static void mlxsw_linecard_provision_clear(struct mlxsw_linecard *linecard) { mlxsw_linecard_status_event_done(linecard, MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION); + mlxsw_linecard_bdev_del(linecard); linecard->provisioned = false; devlink_linecard_provision_clear(linecard->devlink_linecard); } @@ -270,6 +670,10 @@ static int mlxsw_linecard_ready_set(struct mlxsw_linecard *linecard) char mddc_pl[MLXSW_REG_MDDC_LEN]; int err; + err = mlxsw_linecard_device_info_update(linecard); + if (err) + return err; + mlxsw_reg_mddc_pack(mddc_pl, linecard->slot_index, false, true); err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddc), mddc_pl); if (err) @@ -885,6 +1289,7 @@ static void mlxsw_linecard_fini(struct mlxsw_core *mlxsw_core, mlxsw_core_flush_owq(); if (linecard->active) mlxsw_linecard_active_clear(linecard); + mlxsw_linecard_bdev_del(linecard); devlink_linecard_destroy(linecard->devlink_linecard); mutex_destroy(&linecard->lock); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index ddab5476c8b2..f27bdecdf952 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -11364,6 +11364,95 @@ mlxsw_reg_mbct_unpack(const char *payload, u8 *p_slot_index, *p_fsm_state = mlxsw_reg_mbct_fsm_state_get(payload); } +/* MDDT - Management DownStream Device Tunneling Register + * ------------------------------------------------------ + * This register allows to deliver query and request messages (PRM registers, + * commands) to a DownStream device. + */ +#define MLXSW_REG_MDDT_ID 0x9160 +#define MLXSW_REG_MDDT_LEN 0x110 + +MLXSW_REG_DEFINE(mddt, MLXSW_REG_MDDT_ID, MLXSW_REG_MDDT_LEN); + +/* reg_mddt_slot_index + * Slot index. + * Access: Index + */ +MLXSW_ITEM32(reg, mddt, slot_index, 0x00, 8, 4); + +/* reg_mddt_device_index + * Device index. + * Access: Index + */ +MLXSW_ITEM32(reg, mddt, device_index, 0x00, 0, 8); + +/* reg_mddt_read_size + * Read size in D-Words. + * Access: OP + */ +MLXSW_ITEM32(reg, mddt, read_size, 0x04, 24, 8); + +/* reg_mddt_write_size + * Write size in D-Words. + * Access: OP + */ +MLXSW_ITEM32(reg, mddt, write_size, 0x04, 16, 8); + +enum mlxsw_reg_mddt_status { + MLXSW_REG_MDDT_STATUS_OK, +}; + +/* reg_mddt_status + * Return code of the Downstream Device to the register that was sent. + * Access: RO + */ +MLXSW_ITEM32(reg, mddt, status, 0x0C, 24, 8); + +enum mlxsw_reg_mddt_method { + MLXSW_REG_MDDT_METHOD_QUERY, + MLXSW_REG_MDDT_METHOD_WRITE, +}; + +/* reg_mddt_method + * Access: OP + */ +MLXSW_ITEM32(reg, mddt, method, 0x0C, 22, 2); + +/* reg_mddt_register_id + * Access: Index + */ +MLXSW_ITEM32(reg, mddt, register_id, 0x0C, 0, 16); + +#define MLXSW_REG_MDDT_PAYLOAD_OFFSET 0x0C +#define MLXSW_REG_MDDT_PRM_REGISTER_HEADER_LEN 4 + +static inline char *mlxsw_reg_mddt_inner_payload(char *payload) +{ + return payload + MLXSW_REG_MDDT_PAYLOAD_OFFSET + + MLXSW_REG_MDDT_PRM_REGISTER_HEADER_LEN; +} + +static inline void mlxsw_reg_mddt_pack(char *payload, u8 slot_index, + u8 device_index, + enum mlxsw_reg_mddt_method method, + const struct mlxsw_reg_info *reg, + char **inner_payload) +{ + int len = reg->len + MLXSW_REG_MDDT_PRM_REGISTER_HEADER_LEN; + + if (WARN_ON(len + MLXSW_REG_MDDT_PAYLOAD_OFFSET > MLXSW_REG_MDDT_LEN)) + len = MLXSW_REG_MDDT_LEN - MLXSW_REG_MDDT_PAYLOAD_OFFSET; + + MLXSW_REG_ZERO(mddt, payload); + mlxsw_reg_mddt_slot_index_set(payload, slot_index); + mlxsw_reg_mddt_device_index_set(payload, device_index); + mlxsw_reg_mddt_method_set(payload, method); + mlxsw_reg_mddt_register_id_set(payload, reg->id); + mlxsw_reg_mddt_read_size_set(payload, len / 4); + mlxsw_reg_mddt_write_size_set(payload, len / 4); + *inner_payload = mlxsw_reg_mddt_inner_payload(payload); +} + /* MDDQ - Management DownStream Device Query Register * -------------------------------------------------- * This register allows to query the DownStream device properties. The desired @@ -11385,7 +11474,11 @@ MLXSW_ITEM32(reg, mddq, sie, 0x00, 31, 1); enum mlxsw_reg_mddq_query_type { MLXSW_REG_MDDQ_QUERY_TYPE_SLOT_INFO = 1, - MLXSW_REG_MDDQ_QUERY_TYPE_SLOT_NAME = 3, + MLXSW_REG_MDDQ_QUERY_TYPE_DEVICE_INFO, /* If there are no devices + * on the slot, data_valid + * will be '0'. + */ + MLXSW_REG_MDDQ_QUERY_TYPE_SLOT_NAME, }; /* reg_mddq_query_type @@ -11399,6 +11492,28 @@ MLXSW_ITEM32(reg, mddq, query_type, 0x00, 16, 8); */ MLXSW_ITEM32(reg, mddq, slot_index, 0x00, 0, 4); +/* reg_mddq_response_msg_seq + * Response message sequential number. For a specific request, the response + * message sequential number is the following one. In addition, the last + * message should be 0. + * Access: RO + */ +MLXSW_ITEM32(reg, mddq, response_msg_seq, 0x04, 16, 8); + +/* reg_mddq_request_msg_seq + * Request message sequential number. + * The first message number should be 0. + * Access: Index + */ +MLXSW_ITEM32(reg, mddq, request_msg_seq, 0x04, 0, 8); + +/* reg_mddq_data_valid + * If set, the data in the data field is valid and contain the information + * for the queried index. + * Access: RO + */ +MLXSW_ITEM32(reg, mddq, data_valid, 0x08, 31, 1); + /* reg_mddq_slot_info_provisioned * If set, the INI file is applied and the card is provisioned. * Access: RO @@ -11485,6 +11600,61 @@ mlxsw_reg_mddq_slot_info_unpack(const char *payload, u8 *p_slot_index, *p_card_type = mlxsw_reg_mddq_slot_info_card_type_get(payload); } +/* reg_mddq_device_info_flash_owner + * If set, the device is the flash owner. Otherwise, a shared flash + * is used by this device (another device is the flash owner). + * Access: RO + */ +MLXSW_ITEM32(reg, mddq, device_info_flash_owner, 0x10, 30, 1); + +/* reg_mddq_device_info_device_index + * Device index. The first device should number 0. + * Access: RO + */ +MLXSW_ITEM32(reg, mddq, device_info_device_index, 0x10, 0, 8); + +/* reg_mddq_device_info_fw_major + * Major FW version number. + * Access: RO + */ +MLXSW_ITEM32(reg, mddq, device_info_fw_major, 0x14, 16, 16); + +/* reg_mddq_device_info_fw_minor + * Minor FW version number. + * Access: RO + */ +MLXSW_ITEM32(reg, mddq, device_info_fw_minor, 0x18, 16, 16); + +/* reg_mddq_device_info_fw_sub_minor + * Sub-minor FW version number. + * Access: RO + */ +MLXSW_ITEM32(reg, mddq, device_info_fw_sub_minor, 0x18, 0, 16); + +static inline void +mlxsw_reg_mddq_device_info_pack(char *payload, u8 slot_index, + u8 request_msg_seq) +{ + __mlxsw_reg_mddq_pack(payload, slot_index, + MLXSW_REG_MDDQ_QUERY_TYPE_DEVICE_INFO); + mlxsw_reg_mddq_request_msg_seq_set(payload, request_msg_seq); +} + +static inline void +mlxsw_reg_mddq_device_info_unpack(const char *payload, u8 *p_response_msg_seq, + bool *p_data_valid, bool *p_flash_owner, + u8 *p_device_index, u16 *p_fw_major, + u16 *p_fw_minor, u16 *p_fw_sub_minor) +{ + *p_response_msg_seq = mlxsw_reg_mddq_response_msg_seq_get(payload); + *p_data_valid = mlxsw_reg_mddq_data_valid_get(payload); + *p_flash_owner = mlxsw_reg_mddq_device_info_flash_owner_get(payload); + *p_device_index = mlxsw_reg_mddq_device_info_device_index_get(payload); + *p_fw_major = mlxsw_reg_mddq_device_info_fw_major_get(payload); + *p_fw_minor = mlxsw_reg_mddq_device_info_fw_minor_get(payload); + *p_fw_sub_minor = mlxsw_reg_mddq_device_info_fw_sub_minor_get(payload); +} + #define MLXSW_REG_MDDQ_SLOT_ASCII_NAME_LEN 20 /* reg_mddq_slot_ascii_name @@ -12862,6 +13032,7 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = { MLXSW_REG(mfgd), MLXSW_REG(mgpir), MLXSW_REG(mbct), + MLXSW_REG(mddt), MLXSW_REG(mddq), MLXSW_REG(mddc), MLXSW_REG(mfde), diff --git a/include/net/devlink.h b/include/net/devlink.h index 780744b550b8..5bd3fac12e9e 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -1580,6 +1580,8 @@ void devlink_linecard_provision_clear(struct devlink_linecard *linecard); void devlink_linecard_provision_fail(struct devlink_linecard *linecard); void devlink_linecard_activate(struct devlink_linecard *linecard); void devlink_linecard_deactivate(struct devlink_linecard *linecard); +void devlink_linecard_nested_dl_set(struct devlink_linecard *linecard, + struct devlink *nested_devlink); int devl_sb_register(struct devlink *devlink, unsigned int sb_index, u32 size, u16 ingress_pools_count, u16 egress_pools_count, u16 ingress_tc_count, diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h index b3d40a5d72ff..541321695f52 100644 --- a/include/uapi/linux/devlink.h +++ b/include/uapi/linux/devlink.h @@ -576,6 +576,8 @@ enum devlink_attr { DEVLINK_ATTR_LINECARD_TYPE, /* string */ DEVLINK_ATTR_LINECARD_SUPPORTED_TYPES, /* nested */ + DEVLINK_ATTR_NESTED_DEVLINK, /* nested */ + /* add new attributes above here, update the policy in devlink.c */ __DEVLINK_ATTR_MAX, diff --git a/net/core/devlink.c b/net/core/devlink.c index 98d79feeb3dc..698b2d6e0ec7 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -70,6 +70,7 @@ struct devlink { u8 reload_failed:1; refcount_t refcount; struct completion comp; + struct rcu_head rcu; char priv[] __aligned(NETDEV_ALIGN); }; @@ -88,6 +89,7 @@ struct devlink_linecard { const char *type; struct devlink_linecard_type *types; unsigned int types_count; + struct devlink *nested_devlink; }; /** @@ -221,8 +223,6 @@ static DEFINE_XARRAY_FLAGS(devlinks, XA_FLAGS_ALLOC); /* devlink_mutex * * An overall lock guarding every operation coming from userspace. - * It also guards devlink devices list and it is taken when - * driver registers/unregisters it. */ static DEFINE_MUTEX(devlink_mutex); @@ -232,10 +232,21 @@ struct net *devlink_net(const struct devlink *devlink) } EXPORT_SYMBOL_GPL(devlink_net); +static void __devlink_put_rcu(struct rcu_head *head) +{ + struct devlink *devlink = container_of(head, struct devlink, rcu); + + complete(&devlink->comp); +} + void devlink_put(struct devlink *devlink) { if (refcount_dec_and_test(&devlink->refcount)) - complete(&devlink->comp); + /* Make sure unregister operation that may await the completion + * is unblocked only after all users are after the end of + * RCU grace period. + */ + call_rcu(&devlink->rcu, __devlink_put_rcu); } struct devlink *__must_check devlink_try_get(struct devlink *devlink) @@ -278,12 +289,62 @@ void devl_unlock(struct devlink *devlink) } EXPORT_SYMBOL_GPL(devl_unlock); +static struct devlink * +devlinks_xa_find_get(struct net *net, unsigned long *indexp, xa_mark_t filter, + void * (*xa_find_fn)(struct xarray *, unsigned long *, + unsigned long, xa_mark_t)) +{ + struct devlink *devlink; + + rcu_read_lock(); +retry: + devlink = xa_find_fn(&devlinks, indexp, ULONG_MAX, DEVLINK_REGISTERED); + if (!devlink) + goto unlock; + /* For a possible retry, the xa_find_after() should be always used */ + xa_find_fn = xa_find_after; + if (!devlink_try_get(devlink)) + goto retry; + if (!net_eq(devlink_net(devlink), net)) { + devlink_put(devlink); + goto retry; + } +unlock: + rcu_read_unlock(); + return devlink; +} + +static struct devlink *devlinks_xa_find_get_first(struct net *net, + unsigned long *indexp, + xa_mark_t filter) +{ + return devlinks_xa_find_get(net, indexp, filter, xa_find); +} + +static struct devlink *devlinks_xa_find_get_next(struct net *net, + unsigned long *indexp, + xa_mark_t filter) +{ + return devlinks_xa_find_get(net, indexp, filter, xa_find_after); +} + +/* Iterate over devlink pointers which were possible to get reference to. + * devlink_put() needs to be called for each iterated devlink pointer + * in loop body in order to release the reference. + */ +#define devlinks_xa_for_each_get(net, index, devlink, filter) \ + for (index = 0, \ + devlink = devlinks_xa_find_get_first(net, &index, filter); \ + devlink; devlink = devlinks_xa_find_get_next(net, &index, filter)) + +#define devlinks_xa_for_each_registered_get(net, index, devlink) \ + devlinks_xa_for_each_get(net, index, devlink, DEVLINK_REGISTERED) + static struct devlink *devlink_get_from_attrs(struct net *net, struct nlattr **attrs) { struct devlink *devlink; unsigned long index; - bool found = false; char *busname; char *devname; @@ -293,21 +354,14 @@ static struct devlink *devlink_get_from_attrs(struct net *net, busname = nla_data(attrs[DEVLINK_ATTR_BUS_NAME]); devname = nla_data(attrs[DEVLINK_ATTR_DEV_NAME]); - lockdep_assert_held(&devlink_mutex); - - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { + devlinks_xa_for_each_registered_get(net, index, devlink) { if (strcmp(devlink->dev->bus->name, busname) == 0 && - strcmp(dev_name(devlink->dev), devname) == 0 && - net_eq(devlink_net(devlink), net)) { - found = true; - break; - } + strcmp(dev_name(devlink->dev), devname) == 0) + return devlink; + devlink_put(devlink); } - if (!found || !devlink_try_get(devlink)) - devlink = ERR_PTR(-ENODEV); - - return devlink; + return ERR_PTR(-ENODEV); } static struct devlink_port *devlink_port_get_by_index(struct devlink *devlink, @@ -803,6 +857,24 @@ static int devlink_nl_put_handle(struct sk_buff *msg, struct devlink *devlink) return 0; } +static int devlink_nl_put_nested_handle(struct sk_buff *msg, struct devlink *devlink) +{ + struct nlattr *nested_attr; + + nested_attr = nla_nest_start(msg, DEVLINK_ATTR_NESTED_DEVLINK); + if (!nested_attr) + return -EMSGSIZE; + if (devlink_nl_put_handle(msg, devlink)) + goto nla_put_failure; + + nla_nest_end(msg, nested_attr); + return 0; + +nla_put_failure: + nla_nest_cancel(msg, nested_attr); + return -EMSGSIZE; +} + struct devlink_reload_combination { enum devlink_reload_action action; enum devlink_reload_limit limit; @@ -1329,13 +1401,7 @@ static int devlink_nl_cmd_rate_get_dumpit(struct sk_buff *msg, int err = 0; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) - goto retry; - + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { devl_lock(devlink); list_for_each_entry(devlink_rate, &devlink->rate_list, list) { enum devlink_command cmd = DEVLINK_CMD_RATE_NEW; @@ -1356,7 +1422,6 @@ static int devlink_nl_cmd_rate_get_dumpit(struct sk_buff *msg, idx++; } devl_unlock(devlink); -retry: devlink_put(devlink); } out: @@ -1432,15 +1497,7 @@ static int devlink_nl_cmd_get_dumpit(struct sk_buff *msg, int err; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) { - devlink_put(devlink); - continue; - } - + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { if (idx < start) { idx++; devlink_put(devlink); @@ -1495,13 +1552,7 @@ static int devlink_nl_cmd_port_get_dumpit(struct sk_buff *msg, int err; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) - goto retry; - + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { devl_lock(devlink); list_for_each_entry(devlink_port, &devlink->port_list, list) { if (idx < start) { @@ -1521,7 +1572,6 @@ static int devlink_nl_cmd_port_get_dumpit(struct sk_buff *msg, idx++; } devl_unlock(devlink); -retry: devlink_put(devlink); } out: @@ -2104,6 +2154,10 @@ static int devlink_nl_linecard_fill(struct sk_buff *msg, nla_nest_end(msg, attr); } + if (linecard->nested_devlink && + devlink_nl_put_nested_handle(msg, linecard->nested_devlink)) + goto nla_put_failure; + genlmsg_end(msg, hdr); return 0; @@ -2177,13 +2231,7 @@ static int devlink_nl_cmd_linecard_get_dumpit(struct sk_buff *msg, int err; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) - goto retry; - + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { mutex_lock(&devlink->linecards_lock); list_for_each_entry(linecard, &devlink->linecard_list, list) { if (idx < start) { @@ -2206,7 +2254,6 @@ static int devlink_nl_cmd_linecard_get_dumpit(struct sk_buff *msg, idx++; } mutex_unlock(&devlink->linecards_lock); -retry: devlink_put(devlink); } out: @@ -2449,13 +2496,7 @@ static int devlink_nl_cmd_sb_get_dumpit(struct sk_buff *msg, int err; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) - goto retry; - + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { devl_lock(devlink); list_for_each_entry(devlink_sb, &devlink->sb_list, list) { if (idx < start) { @@ -2475,7 +2516,6 @@ static int devlink_nl_cmd_sb_get_dumpit(struct sk_buff *msg, idx++; } devl_unlock(devlink); -retry: devlink_put(devlink); } out: @@ -2601,10 +2641,7 @@ static int devlink_nl_cmd_sb_pool_get_dumpit(struct sk_buff *msg, int err = 0; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { if (!net_eq(devlink_net(devlink), sock_net(msg->sk)) || !devlink->ops->sb_pool_get) goto retry; @@ -2822,12 +2859,8 @@ static int devlink_nl_cmd_sb_port_pool_get_dumpit(struct sk_buff *msg, int err = 0; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk)) || - !devlink->ops->sb_port_pool_get) + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { + if (!devlink->ops->sb_port_pool_get) goto retry; devl_lock(devlink); @@ -3071,12 +3104,8 @@ devlink_nl_cmd_sb_tc_pool_bind_get_dumpit(struct sk_buff *msg, int err = 0; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk)) || - !devlink->ops->sb_tc_pool_bind_get) + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { + if (!devlink->ops->sb_tc_pool_bind_get) goto retry; devl_lock(devlink); @@ -5158,13 +5187,7 @@ static int devlink_nl_cmd_param_get_dumpit(struct sk_buff *msg, int err = 0; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) - goto retry; - + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { devl_lock(devlink); list_for_each_entry(param_item, &devlink->param_list, list) { if (idx < start) { @@ -5186,7 +5209,6 @@ static int devlink_nl_cmd_param_get_dumpit(struct sk_buff *msg, idx++; } devl_unlock(devlink); -retry: devlink_put(devlink); } out: @@ -5393,13 +5415,7 @@ static int devlink_nl_cmd_port_param_get_dumpit(struct sk_buff *msg, int err = 0; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) - goto retry; - + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { devl_lock(devlink); list_for_each_entry(devlink_port, &devlink->port_list, list) { list_for_each_entry(param_item, @@ -5426,7 +5442,6 @@ static int devlink_nl_cmd_port_param_get_dumpit(struct sk_buff *msg, } } devl_unlock(devlink); -retry: devlink_put(devlink); } out: @@ -5977,16 +5992,9 @@ static int devlink_nl_cmd_region_get_dumpit(struct sk_buff *msg, int err = 0; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) - goto retry; - + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { err = devlink_nl_cmd_region_get_devlink_dumpit(msg, cb, devlink, &idx, start); -retry: devlink_put(devlink); if (err) goto out; @@ -6511,13 +6519,7 @@ static int devlink_nl_cmd_info_get_dumpit(struct sk_buff *msg, int err = 0; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) - goto retry; - + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { if (idx < start || !devlink->ops->info_get) goto inc; @@ -6535,7 +6537,6 @@ static int devlink_nl_cmd_info_get_dumpit(struct sk_buff *msg, } inc: idx++; -retry: devlink_put(devlink); } mutex_unlock(&devlink_mutex); @@ -7691,13 +7692,7 @@ devlink_nl_cmd_health_reporter_get_dumpit(struct sk_buff *msg, int err; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) - goto retry_rep; - + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { mutex_lock(&devlink->reporters_lock); list_for_each_entry(reporter, &devlink->reporter_list, list) { @@ -7717,17 +7712,10 @@ devlink_nl_cmd_health_reporter_get_dumpit(struct sk_buff *msg, idx++; } mutex_unlock(&devlink->reporters_lock); -retry_rep: devlink_put(devlink); } - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) - goto retry_port; - + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { devl_lock(devlink); list_for_each_entry(port, &devlink->port_list, list) { mutex_lock(&port->reporters_lock); @@ -7752,7 +7740,6 @@ retry_rep: mutex_unlock(&port->reporters_lock); } devl_unlock(devlink); -retry_port: devlink_put(devlink); } out: @@ -8291,13 +8278,7 @@ static int devlink_nl_cmd_trap_get_dumpit(struct sk_buff *msg, int err; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) - goto retry; - + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { devl_lock(devlink); list_for_each_entry(trap_item, &devlink->trap_list, list) { if (idx < start) { @@ -8317,7 +8298,6 @@ static int devlink_nl_cmd_trap_get_dumpit(struct sk_buff *msg, idx++; } devl_unlock(devlink); -retry: devlink_put(devlink); } out: @@ -8518,13 +8498,7 @@ static int devlink_nl_cmd_trap_group_get_dumpit(struct sk_buff *msg, int err; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) - goto retry; - + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { devl_lock(devlink); list_for_each_entry(group_item, &devlink->trap_group_list, list) { @@ -8545,7 +8519,6 @@ static int devlink_nl_cmd_trap_group_get_dumpit(struct sk_buff *msg, idx++; } devl_unlock(devlink); -retry: devlink_put(devlink); } out: @@ -8832,13 +8805,7 @@ static int devlink_nl_cmd_trap_policer_get_dumpit(struct sk_buff *msg, int err; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) - goto retry; - + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { devl_lock(devlink); list_for_each_entry(policer_item, &devlink->trap_policer_list, list) { @@ -8859,7 +8826,6 @@ static int devlink_nl_cmd_trap_policer_get_dumpit(struct sk_buff *msg, idx++; } devl_unlock(devlink); -retry: devlink_put(devlink); } out: @@ -9589,10 +9555,8 @@ void devlink_register(struct devlink *devlink) ASSERT_DEVLINK_NOT_REGISTERED(devlink); /* Make sure that we are in .probe() routine */ - mutex_lock(&devlink_mutex); xa_set_mark(&devlinks, devlink->index, DEVLINK_REGISTERED); devlink_notify_register(devlink); - mutex_unlock(&devlink_mutex); } EXPORT_SYMBOL_GPL(devlink_register); @@ -9609,10 +9573,8 @@ void devlink_unregister(struct devlink *devlink) devlink_put(devlink); wait_for_completion(&devlink->comp); - mutex_lock(&devlink_mutex); devlink_notify_unregister(devlink); xa_clear_mark(&devlinks, devlink->index, DEVLINK_REGISTERED); - mutex_unlock(&devlink_mutex); } EXPORT_SYMBOL_GPL(devlink_unregister); @@ -10316,6 +10278,7 @@ EXPORT_SYMBOL_GPL(devlink_linecard_provision_set); void devlink_linecard_provision_clear(struct devlink_linecard *linecard) { mutex_lock(&linecard->state_lock); + WARN_ON(linecard->nested_devlink); linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED; linecard->type = NULL; devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); @@ -10334,6 +10297,7 @@ EXPORT_SYMBOL_GPL(devlink_linecard_provision_clear); void devlink_linecard_provision_fail(struct devlink_linecard *linecard) { mutex_lock(&linecard->state_lock); + WARN_ON(linecard->nested_devlink); linecard->state = DEVLINK_LINECARD_STATE_PROVISIONING_FAILED; devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); mutex_unlock(&linecard->state_lock); @@ -10381,6 +10345,23 @@ void devlink_linecard_deactivate(struct devlink_linecard *linecard) } EXPORT_SYMBOL_GPL(devlink_linecard_deactivate); +/** + * devlink_linecard_nested_dl_set - Attach/detach nested devlink + * instance to linecard. + * + * @linecard: devlink linecard + * @nested_devlink: devlink instance to attach or NULL to detach + */ +void devlink_linecard_nested_dl_set(struct devlink_linecard *linecard, + struct devlink *nested_devlink) +{ + mutex_lock(&linecard->state_lock); + linecard->nested_devlink = nested_devlink; + devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); + mutex_unlock(&linecard->state_lock); +} +EXPORT_SYMBOL_GPL(devlink_linecard_nested_dl_set); + int devl_sb_register(struct devlink *devlink, unsigned int sb_index, u32 size, u16 ingress_pools_count, u16 egress_pools_count, u16 ingress_tc_count, @@ -12281,13 +12262,7 @@ static void __net_exit devlink_pernet_pre_exit(struct net *net) * all devlink instances from this namespace into init_net. */ mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), net)) - goto retry; - + devlinks_xa_for_each_registered_get(net, index, devlink) { WARN_ON(!(devlink->features & DEVLINK_F_RELOAD)); err = devlink_reload(devlink, &init_net, DEVLINK_RELOAD_ACTION_DRIVER_REINIT, @@ -12295,7 +12270,6 @@ static void __net_exit devlink_pernet_pre_exit(struct net *net) &actions_performed, NULL); if (err && err != -EOPNOTSUPP) pr_warn("Failed to reload devlink instance into init_net\n"); -retry: devlink_put(devlink); } mutex_unlock(&devlink_mutex); diff --git a/tools/testing/selftests/drivers/net/mlxsw/devlink_linecard.sh b/tools/testing/selftests/drivers/net/mlxsw/devlink_linecard.sh index 08a922d8b86a..224ca3695c89 100755 --- a/tools/testing/selftests/drivers/net/mlxsw/devlink_linecard.sh +++ b/tools/testing/selftests/drivers/net/mlxsw/devlink_linecard.sh @@ -84,6 +84,13 @@ lc_wait_until_port_count_is() busywait "$timeout" until_lc_port_count_is "$port_count" lc_port_count_get "$lc" } +lc_nested_devlink_dev_get() +{ + local lc=$1 + + devlink lc show $DEVLINK_DEV lc $lc -j | jq -e -r ".[][][].nested_devlink" +} + PROV_UNPROV_TIMEOUT=8000 # ms POST_PROV_ACT_TIMEOUT=2000 # ms PROV_PORTS_INSTANTIATION_TIMEOUT=15000 # ms @@ -191,12 +198,30 @@ ports_check() check_err $? "Unexpected port count linecard $lc (got $port_count, expected $expected_port_count)" } +lc_dev_info_provisioned_check() +{ + local lc=$1 + local nested_devlink_dev=$2 + local fixed_hw_revision + local running_ini_version + + fixed_hw_revision=$(devlink dev info $nested_devlink_dev -j | \ + jq -e -r '.[][].versions.fixed."hw.revision"') + check_err $? "Failed to get linecard $lc fixed.hw.revision" + log_info "Linecard $lc fixed.hw.revision: \"$fixed_hw_revision\"" + running_ini_version=$(devlink dev info $nested_devlink_dev -j | \ + jq -e -r '.[][].versions.running."ini.version"') + check_err $? "Failed to get linecard $lc running.ini.version" + log_info "Linecard $lc running.ini.version: \"$running_ini_version\"" +} + provision_test() { RET=0 local lc local type local state + local nested_devlink_dev lc=$LC_SLOT supported_types_check $lc @@ -207,6 +232,11 @@ provision_test() fi provision_one $lc $LC_16X100G_TYPE ports_check $lc $LC_16X100G_PORT_COUNT + + nested_devlink_dev=$(lc_nested_devlink_dev_get $lc) + check_err $? "Failed to get nested devlink handle of linecard $lc" + lc_dev_info_provisioned_check $lc $nested_devlink_dev + log_test "Provision" } @@ -220,12 +250,32 @@ interface_check() setup_wait } +lc_dev_info_active_check() +{ + local lc=$1 + local nested_devlink_dev=$2 + local fixed_device_fw_psid + local running_device_fw + + fixed_device_fw_psid=$(devlink dev info $nested_devlink_dev -j | \ + jq -e -r ".[][].versions.fixed" | \ + jq -e -r '."fw.psid"') + check_err $? "Failed to get linecard $lc fixed fw PSID" + log_info "Linecard $lc fixed.fw.psid: \"$fixed_device_fw_psid\"" + + running_device_fw=$(devlink dev info $nested_devlink_dev -j | \ + jq -e -r ".[][].versions.running.fw") + check_err $? "Failed to get linecard $lc running.fw.version" + log_info "Linecard $lc running.fw: \"$running_device_fw\"" +} + activation_16x100G_test() { RET=0 local lc local type local state + local nested_devlink_dev lc=$LC_SLOT type=$LC_16X100G_TYPE @@ -238,6 +288,10 @@ activation_16x100G_test() interface_check + nested_devlink_dev=$(lc_nested_devlink_dev_get $lc) + check_err $? "Failed to get nested devlink handle of linecard $lc" + lc_dev_info_active_check $lc $nested_devlink_dev + log_test "Activation 16x100G" } |