Commit 4fd310c7 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'implement-dev-info-and-dev-flash-for-line-cards'

Jiri Pirko says:

====================
Implement dev info and dev flash for line cards

This patchset implements two features:
1) "devlink dev info" is exposed for line card (patches 6-9)
2) "devlink dev flash" is implemented for line card gearbox
   flashing (patch 10)

For every line card, "a nested" auxiliary device is created which
allows to bind the features mentioned above (patch 4).

The relationship between line card and its auxiliary dev devlink
is carried over extra line card netlink attribute (patches 3 and 5).

The first patch removes devlink_mutex from devlink_register/unregister()
which eliminates possible deadlock during devlink reload command. The
second patchset follows up with putting net pointer check into new
helper.

Examples:

$ devlink lc show pci/0000:01:00.0 lc 1
pci/0000:01:00.0:
  lc 1 state active type 16x100G nested_devlink auxiliary/mlxsw_core.lc.0
    supported_types:
       16x100G

$ devlink dev show auxiliary/mlxsw_core.lc.0
auxiliary/mlxsw_core.lc.0

$ devlink dev info auxiliary/mlxsw_core.lc.0
auxiliary/mlxsw_core.lc.0:
  versions:
      fixed:
        hw.revision 0
        fw.psid MT_0000000749
      running:
        ini.version 4
        fw 19.2010.1312

$ devlink dev flash auxiliary/mlxsw_core.lc.0 file mellanox/fw-AGB-rel-19_2010_1312-022-EVB.mfa2
====================

Link: https://lore.kernel.org/r/20220725082925.366455-1-jiri@resnulli.usSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 35d099da 949c84f0
......@@ -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
=====================
......
......@@ -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.
......
......@@ -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
......
......@@ -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);
......
......@@ -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
// 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);
}
......@@ -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);
}
......
......@@ -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),
......
......@@ -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,
......
......@@ -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,
......
......@@ -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 @@ devlink_nl_cmd_health_reporter_get_dumpit(struct sk_buff *msg,
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);
......
......@@ -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"
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment