mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-18 20:31:06 -04:00
Merge branch 'devlink-add-per-port-resource-support'
Tariq Toukan says: ==================== devlink: add per-port resource support This series by Or adds devlink per-port resource support: Currently, devlink resources are only available at the device level. However, some resources are inherently per-port, such as the maximum number of subfunctions (SFs) that can be created on a specific PF port. This limitation prevents user space from obtaining accurate per-port capacity information. This series adds infrastructure for per-port resources in devlink core and implements it in the mlx5 driver to expose the max_SFs resource on PF devlink ports. Patch #1 refactors resource functions to be generic Patch #2 adds port-level resource registration infrastructure Patch #3 registers SF resource on PF port representor in mlx5 Patch #4 adds devlink port resource registration to netdevsim for testing Patch #5 adds dump support for device-level resources Patch #6 includes port resources in the resource dump dumpit path Patch #7 adds port-specific option to resource dump doit path Patch #8 adds selftest for devlink port resource doit Patch #9 documents port-level resources and full dump Patch #10 adds resource scope filtering to resource dump Patch #11 adds selftest for resource dump and scope filter Patch #12 documents resource scope filtering ==================== Link: https://patch.msgid.link/20260407194107.148063-1-tariqt@nvidia.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -157,6 +157,14 @@ definitions:
|
||||
entries:
|
||||
-
|
||||
name: entry
|
||||
-
|
||||
type: enum
|
||||
name: resource-scope
|
||||
entries:
|
||||
-
|
||||
name: dev
|
||||
-
|
||||
name: port
|
||||
-
|
||||
type: enum
|
||||
name: reload-action
|
||||
@@ -873,6 +881,16 @@ attribute-sets:
|
||||
doc: Unique devlink instance index.
|
||||
checks:
|
||||
max: u32-max
|
||||
-
|
||||
name: resource-scope-mask
|
||||
type: u32
|
||||
enum: resource-scope
|
||||
enum-as-flags: true
|
||||
doc: |
|
||||
Bitmask selecting which resource classes to include in a
|
||||
resource-dump response. Bit 0 (dev) selects device-level
|
||||
resources; bit 1 (port) selects port-level resources.
|
||||
When absent all classes are returned.
|
||||
-
|
||||
name: dl-dev-stats
|
||||
subset-of: devlink
|
||||
@@ -1757,20 +1775,30 @@ operations:
|
||||
attribute-set: devlink
|
||||
dont-validate: [strict]
|
||||
do:
|
||||
pre: devlink-nl-pre-doit
|
||||
pre: devlink-nl-pre-doit-port-optional
|
||||
post: devlink-nl-post-doit
|
||||
request:
|
||||
attributes:
|
||||
- bus-name
|
||||
- dev-name
|
||||
- index
|
||||
reply:
|
||||
- port-index
|
||||
reply: &resource-dump-reply
|
||||
value: 36
|
||||
attributes:
|
||||
- bus-name
|
||||
- dev-name
|
||||
- index
|
||||
- port-index
|
||||
- resource-list
|
||||
dump:
|
||||
request:
|
||||
attributes:
|
||||
- bus-name
|
||||
- dev-name
|
||||
- index
|
||||
- resource-scope-mask
|
||||
reply: *resource-dump-reply
|
||||
|
||||
-
|
||||
name: reload
|
||||
|
||||
@@ -74,3 +74,73 @@ attribute, which represents the pending change in size. For example:
|
||||
|
||||
Note that changes in resource size may require a device reload to properly
|
||||
take effect.
|
||||
|
||||
Port-level Resources and Full Dump
|
||||
==================================
|
||||
|
||||
In addition to device-level resources, ``devlink`` also supports port-level
|
||||
resources. These resources are associated with a specific devlink port rather
|
||||
than the device as a whole.
|
||||
|
||||
To list resources for all devlink devices and ports:
|
||||
|
||||
.. code:: shell
|
||||
|
||||
$ devlink resource show
|
||||
pci/0000:03:00.0:
|
||||
name max_local_SFs size 128 unit entry dpipe_tables none
|
||||
name max_external_SFs size 128 unit entry dpipe_tables none
|
||||
pci/0000:03:00.0/196608:
|
||||
name max_SFs size 128 unit entry dpipe_tables none
|
||||
pci/0000:03:00.0/196609:
|
||||
name max_SFs size 128 unit entry dpipe_tables none
|
||||
pci/0000:03:00.1:
|
||||
name max_local_SFs size 128 unit entry dpipe_tables none
|
||||
name max_external_SFs size 128 unit entry dpipe_tables none
|
||||
pci/0000:03:00.1/196708:
|
||||
name max_SFs size 128 unit entry dpipe_tables none
|
||||
pci/0000:03:00.1/196709:
|
||||
name max_SFs size 128 unit entry dpipe_tables none
|
||||
|
||||
To show resources for a specific port:
|
||||
|
||||
.. code:: shell
|
||||
|
||||
$ devlink resource show pci/0000:03:00.0/196608
|
||||
pci/0000:03:00.0/196608:
|
||||
name max_SFs size 128 unit entry dpipe_tables none
|
||||
|
||||
Resource Scope Filtering
|
||||
========================
|
||||
|
||||
When dumping resources for all devices, ``devlink resource show`` accepts
|
||||
an optional ``scope`` parameter to restrict the response to device-level
|
||||
resources, port-level resources, or both (the default).
|
||||
|
||||
To dump only device-level resources across all devices:
|
||||
|
||||
.. code:: shell
|
||||
|
||||
$ devlink resource show scope dev
|
||||
pci/0000:03:00.0:
|
||||
name max_local_SFs size 128 unit entry dpipe_tables none
|
||||
name max_external_SFs size 128 unit entry dpipe_tables none
|
||||
pci/0000:03:00.1:
|
||||
name max_local_SFs size 128 unit entry dpipe_tables none
|
||||
name max_external_SFs size 128 unit entry dpipe_tables none
|
||||
|
||||
To dump only port-level resources across all devices:
|
||||
|
||||
.. code:: shell
|
||||
|
||||
$ devlink resource show scope port
|
||||
pci/0000:03:00.0/196608:
|
||||
name max_SFs size 128 unit entry dpipe_tables none
|
||||
pci/0000:03:00.0/196609:
|
||||
name max_SFs size 128 unit entry dpipe_tables none
|
||||
pci/0000:03:00.1/196708:
|
||||
name max_SFs size 128 unit entry dpipe_tables none
|
||||
pci/0000:03:00.1/196709:
|
||||
name max_SFs size 128 unit entry dpipe_tables none
|
||||
|
||||
Note that port-level resources are read-only.
|
||||
|
||||
@@ -14,6 +14,10 @@ enum mlx5_devlink_resource_id {
|
||||
MLX5_ID_RES_MAX = __MLX5_ID_RES_MAX - 1,
|
||||
};
|
||||
|
||||
enum mlx5_devlink_port_resource_id {
|
||||
MLX5_DL_PORT_RES_MAX_SFS = 1,
|
||||
};
|
||||
|
||||
enum mlx5_devlink_param_id {
|
||||
MLX5_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
|
||||
MLX5_DEVLINK_PARAM_ID_FLOW_STEERING_MODE,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <linux/mlx5/driver.h>
|
||||
#include "eswitch.h"
|
||||
#include "devlink.h"
|
||||
|
||||
static void
|
||||
mlx5_esw_get_port_parent_id(struct mlx5_core_dev *dev, struct netdev_phys_item_id *ppid)
|
||||
@@ -158,6 +159,32 @@ static const struct devlink_port_ops mlx5_esw_dl_sf_port_ops = {
|
||||
.port_fn_max_io_eqs_set = mlx5_devlink_port_fn_max_io_eqs_set,
|
||||
};
|
||||
|
||||
static int mlx5_esw_devlink_port_res_register(struct mlx5_eswitch *esw,
|
||||
struct devlink_port *dl_port)
|
||||
{
|
||||
struct devlink_resource_size_params size_params;
|
||||
struct mlx5_core_dev *dev = esw->dev;
|
||||
u16 max_sfs, sf_base_id;
|
||||
int err;
|
||||
|
||||
err = mlx5_esw_sf_max_hpf_functions(dev, &max_sfs, &sf_base_id);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
devlink_resource_size_params_init(&size_params, max_sfs, max_sfs, 1,
|
||||
DEVLINK_RESOURCE_UNIT_ENTRY);
|
||||
|
||||
return devl_port_resource_register(dl_port, "max_SFs", max_sfs,
|
||||
MLX5_DL_PORT_RES_MAX_SFS,
|
||||
DEVLINK_RESOURCE_ID_PARENT_TOP,
|
||||
&size_params);
|
||||
}
|
||||
|
||||
static void mlx5_esw_devlink_port_res_unregister(struct devlink_port *dl_port)
|
||||
{
|
||||
devl_port_resources_unregister(dl_port);
|
||||
}
|
||||
|
||||
int mlx5_esw_offloads_devlink_port_register(struct mlx5_eswitch *esw, struct mlx5_vport *vport)
|
||||
{
|
||||
struct mlx5_core_dev *dev = esw->dev;
|
||||
@@ -189,6 +216,15 @@ int mlx5_esw_offloads_devlink_port_register(struct mlx5_eswitch *esw, struct mlx
|
||||
if (err)
|
||||
goto rate_err;
|
||||
|
||||
if (vport_num == MLX5_VPORT_PF) {
|
||||
err = mlx5_esw_devlink_port_res_register(esw,
|
||||
&dl_port->dl_port);
|
||||
if (err)
|
||||
mlx5_core_dbg(dev,
|
||||
"Failed to register port resources: %d\n",
|
||||
err);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
rate_err:
|
||||
@@ -203,6 +239,7 @@ void mlx5_esw_offloads_devlink_port_unregister(struct mlx5_vport *vport)
|
||||
if (!vport->dl_port)
|
||||
return;
|
||||
dl_port = vport->dl_port;
|
||||
mlx5_esw_devlink_port_res_unregister(&dl_port->dl_port);
|
||||
|
||||
mlx5_esw_qos_vport_update_parent(vport, NULL, NULL);
|
||||
devl_rate_leaf_destroy(&dl_port->dl_port);
|
||||
|
||||
@@ -1486,9 +1486,25 @@ static int __nsim_dev_port_add(struct nsim_dev *nsim_dev, enum nsim_dev_port_typ
|
||||
if (err)
|
||||
goto err_port_free;
|
||||
|
||||
if (nsim_dev_port_is_pf(nsim_dev_port)) {
|
||||
u64 parent_id = DEVLINK_RESOURCE_ID_PARENT_TOP;
|
||||
struct devlink_resource_size_params params = {
|
||||
.size_max = 100,
|
||||
.size_granularity = 1,
|
||||
.unit = DEVLINK_RESOURCE_UNIT_ENTRY
|
||||
};
|
||||
|
||||
err = devl_port_resource_register(devlink_port,
|
||||
"test_resource", 20,
|
||||
NSIM_PORT_RESOURCE_TEST,
|
||||
parent_id, ¶ms);
|
||||
if (err)
|
||||
goto err_dl_port_unregister;
|
||||
}
|
||||
|
||||
err = nsim_dev_port_debugfs_init(nsim_dev, nsim_dev_port);
|
||||
if (err)
|
||||
goto err_dl_port_unregister;
|
||||
goto err_port_resource_unregister;
|
||||
|
||||
nsim_dev_port->ns = nsim_create(nsim_dev, nsim_dev_port, perm_addr);
|
||||
if (IS_ERR(nsim_dev_port->ns)) {
|
||||
@@ -1511,6 +1527,9 @@ static int __nsim_dev_port_add(struct nsim_dev *nsim_dev, enum nsim_dev_port_typ
|
||||
nsim_destroy(nsim_dev_port->ns);
|
||||
err_port_debugfs_exit:
|
||||
nsim_dev_port_debugfs_exit(nsim_dev_port);
|
||||
err_port_resource_unregister:
|
||||
if (nsim_dev_port_is_pf(nsim_dev_port))
|
||||
devl_port_resources_unregister(devlink_port);
|
||||
err_dl_port_unregister:
|
||||
devl_port_unregister(devlink_port);
|
||||
err_port_free:
|
||||
@@ -1527,6 +1546,8 @@ static void __nsim_dev_port_del(struct nsim_dev_port *nsim_dev_port)
|
||||
devl_rate_leaf_destroy(&nsim_dev_port->devlink_port);
|
||||
nsim_destroy(nsim_dev_port->ns);
|
||||
nsim_dev_port_debugfs_exit(nsim_dev_port);
|
||||
if (nsim_dev_port_is_pf(nsim_dev_port))
|
||||
devl_port_resources_unregister(devlink_port);
|
||||
devl_port_unregister(devlink_port);
|
||||
kfree(nsim_dev_port);
|
||||
}
|
||||
|
||||
@@ -224,6 +224,10 @@ enum nsim_resource_id {
|
||||
NSIM_RESOURCE_NEXTHOPS,
|
||||
};
|
||||
|
||||
enum nsim_port_resource_id {
|
||||
NSIM_PORT_RESOURCE_TEST = 1,
|
||||
};
|
||||
|
||||
struct nsim_dev_health {
|
||||
struct devlink_health_reporter *empty_reporter;
|
||||
struct devlink_health_reporter *dummy_reporter;
|
||||
|
||||
@@ -129,6 +129,7 @@ struct devlink_rate {
|
||||
struct devlink_port {
|
||||
struct list_head list;
|
||||
struct list_head region_list;
|
||||
struct list_head resource_list;
|
||||
struct devlink *devlink;
|
||||
const struct devlink_port_ops *ops;
|
||||
unsigned int index;
|
||||
@@ -1885,12 +1886,19 @@ int devl_resource_register(struct devlink *devlink,
|
||||
u64 resource_size,
|
||||
u64 resource_id,
|
||||
u64 parent_resource_id,
|
||||
const struct devlink_resource_size_params *size_params);
|
||||
const struct devlink_resource_size_params *params);
|
||||
void devl_resources_unregister(struct devlink *devlink);
|
||||
void devlink_resources_unregister(struct devlink *devlink);
|
||||
int devl_resource_size_get(struct devlink *devlink,
|
||||
u64 resource_id,
|
||||
u64 *p_resource_size);
|
||||
int
|
||||
devl_port_resource_register(struct devlink_port *devlink_port,
|
||||
const char *resource_name,
|
||||
u64 resource_size, u64 resource_id,
|
||||
u64 parent_resource_id,
|
||||
const struct devlink_resource_size_params *params);
|
||||
void devl_port_resources_unregister(struct devlink_port *devlink_port);
|
||||
int devl_dpipe_table_resource_set(struct devlink *devlink,
|
||||
const char *table_name, u64 resource_id,
|
||||
u64 resource_units);
|
||||
|
||||
@@ -645,6 +645,7 @@ enum devlink_attr {
|
||||
DEVLINK_ATTR_PARAM_RESET_DEFAULT, /* flag */
|
||||
|
||||
DEVLINK_ATTR_INDEX, /* uint */
|
||||
DEVLINK_ATTR_RESOURCE_SCOPE_MASK, /* u32 */
|
||||
|
||||
/* Add new attributes above here, update the spec in
|
||||
* Documentation/netlink/specs/devlink.yaml and re-generate
|
||||
@@ -704,6 +705,16 @@ enum devlink_resource_unit {
|
||||
DEVLINK_RESOURCE_UNIT_ENTRY,
|
||||
};
|
||||
|
||||
enum devlink_resource_scope {
|
||||
DEVLINK_RESOURCE_SCOPE_DEV_BIT,
|
||||
DEVLINK_RESOURCE_SCOPE_PORT_BIT,
|
||||
};
|
||||
|
||||
#define DEVLINK_RESOURCE_SCOPE_DEV \
|
||||
_BITUL(DEVLINK_RESOURCE_SCOPE_DEV_BIT)
|
||||
#define DEVLINK_RESOURCE_SCOPE_PORT \
|
||||
_BITUL(DEVLINK_RESOURCE_SCOPE_PORT_BIT)
|
||||
|
||||
enum devlink_port_fn_attr_cap {
|
||||
DEVLINK_PORT_FN_ATTR_CAP_ROCE_BIT,
|
||||
DEVLINK_PORT_FN_ATTR_CAP_MIGRATABLE_BIT,
|
||||
|
||||
@@ -164,6 +164,11 @@ struct devlink_nl_dump_state {
|
||||
struct {
|
||||
u64 dump_ts;
|
||||
};
|
||||
/* DEVLINK_CMD_RESOURCE_DUMP */
|
||||
struct {
|
||||
u32 index;
|
||||
bool index_valid;
|
||||
} port_ctx;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -370,6 +370,8 @@ static int devlink_nl_inst_iter_dumpit(struct sk_buff *msg,
|
||||
|
||||
/* restart sub-object walk for the next instance */
|
||||
state->idx = 0;
|
||||
state->port_ctx.index = 0;
|
||||
state->port_ctx.index_valid = false;
|
||||
}
|
||||
|
||||
if (err != -EMSGSIZE)
|
||||
|
||||
@@ -305,10 +305,19 @@ static const struct nla_policy devlink_resource_set_nl_policy[DEVLINK_ATTR_INDEX
|
||||
};
|
||||
|
||||
/* DEVLINK_CMD_RESOURCE_DUMP - do */
|
||||
static const struct nla_policy devlink_resource_dump_nl_policy[DEVLINK_ATTR_INDEX + 1] = {
|
||||
static const struct nla_policy devlink_resource_dump_do_nl_policy[DEVLINK_ATTR_INDEX + 1] = {
|
||||
[DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING, },
|
||||
[DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING, },
|
||||
[DEVLINK_ATTR_INDEX] = NLA_POLICY_FULL_RANGE(NLA_UINT, &devlink_attr_index_range),
|
||||
[DEVLINK_ATTR_PORT_INDEX] = { .type = NLA_U32, },
|
||||
};
|
||||
|
||||
/* DEVLINK_CMD_RESOURCE_DUMP - dump */
|
||||
static const struct nla_policy devlink_resource_dump_dump_nl_policy[DEVLINK_ATTR_RESOURCE_SCOPE_MASK + 1] = {
|
||||
[DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING, },
|
||||
[DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING, },
|
||||
[DEVLINK_ATTR_INDEX] = NLA_POLICY_FULL_RANGE(NLA_UINT, &devlink_attr_index_range),
|
||||
[DEVLINK_ATTR_RESOURCE_SCOPE_MASK] = NLA_POLICY_MASK(NLA_U32, 0x3),
|
||||
};
|
||||
|
||||
/* DEVLINK_CMD_RELOAD - do */
|
||||
@@ -680,7 +689,7 @@ static const struct nla_policy devlink_notify_filter_set_nl_policy[DEVLINK_ATTR_
|
||||
};
|
||||
|
||||
/* Ops table for devlink */
|
||||
const struct genl_split_ops devlink_nl_ops[74] = {
|
||||
const struct genl_split_ops devlink_nl_ops[75] = {
|
||||
{
|
||||
.cmd = DEVLINK_CMD_GET,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT,
|
||||
@@ -955,13 +964,20 @@ const struct genl_split_ops devlink_nl_ops[74] = {
|
||||
{
|
||||
.cmd = DEVLINK_CMD_RESOURCE_DUMP,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT,
|
||||
.pre_doit = devlink_nl_pre_doit,
|
||||
.pre_doit = devlink_nl_pre_doit_port_optional,
|
||||
.doit = devlink_nl_resource_dump_doit,
|
||||
.post_doit = devlink_nl_post_doit,
|
||||
.policy = devlink_resource_dump_nl_policy,
|
||||
.policy = devlink_resource_dump_do_nl_policy,
|
||||
.maxattr = DEVLINK_ATTR_INDEX,
|
||||
.flags = GENL_CMD_CAP_DO,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_RESOURCE_DUMP,
|
||||
.dumpit = devlink_nl_resource_dump_dumpit,
|
||||
.policy = devlink_resource_dump_dump_nl_policy,
|
||||
.maxattr = DEVLINK_ATTR_RESOURCE_SCOPE_MASK,
|
||||
.flags = GENL_CMD_CAP_DUMP,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_RELOAD,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT,
|
||||
|
||||
@@ -18,17 +18,17 @@ extern const struct nla_policy devlink_dl_rate_tc_bws_nl_policy[DEVLINK_RATE_TC_
|
||||
extern const struct nla_policy devlink_dl_selftest_id_nl_policy[DEVLINK_ATTR_SELFTEST_ID_FLASH + 1];
|
||||
|
||||
/* Ops table for devlink */
|
||||
extern const struct genl_split_ops devlink_nl_ops[74];
|
||||
extern const struct genl_split_ops devlink_nl_ops[75];
|
||||
|
||||
int devlink_nl_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
int devlink_nl_pre_doit_port(const struct genl_split_ops *ops,
|
||||
struct sk_buff *skb, struct genl_info *info);
|
||||
int devlink_nl_pre_doit_dev_lock(const struct genl_split_ops *ops,
|
||||
struct sk_buff *skb, struct genl_info *info);
|
||||
int devlink_nl_pre_doit_port_optional(const struct genl_split_ops *ops,
|
||||
struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
int devlink_nl_pre_doit_dev_lock(const struct genl_split_ops *ops,
|
||||
struct sk_buff *skb, struct genl_info *info);
|
||||
void
|
||||
devlink_nl_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
@@ -80,6 +80,8 @@ int devlink_nl_dpipe_table_counters_set_doit(struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
int devlink_nl_resource_set_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
int devlink_nl_resource_dump_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
int devlink_nl_resource_dump_dumpit(struct sk_buff *skb,
|
||||
struct netlink_callback *cb);
|
||||
int devlink_nl_reload_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
int devlink_nl_param_get_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
int devlink_nl_param_get_dumpit(struct sk_buff *skb,
|
||||
|
||||
@@ -1025,6 +1025,7 @@ void devlink_port_init(struct devlink *devlink,
|
||||
return;
|
||||
devlink_port->devlink = devlink;
|
||||
INIT_LIST_HEAD(&devlink_port->region_list);
|
||||
INIT_LIST_HEAD(&devlink_port->resource_list);
|
||||
devlink_port->initialized = true;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devlink_port_init);
|
||||
@@ -1042,6 +1043,7 @@ EXPORT_SYMBOL_GPL(devlink_port_init);
|
||||
void devlink_port_fini(struct devlink_port *devlink_port)
|
||||
{
|
||||
WARN_ON(!list_empty(&devlink_port->region_list));
|
||||
WARN_ON(!list_empty(&devlink_port->resource_list));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devlink_port_fini);
|
||||
|
||||
|
||||
@@ -36,15 +36,16 @@ struct devlink_resource {
|
||||
};
|
||||
|
||||
static struct devlink_resource *
|
||||
devlink_resource_find(struct devlink *devlink,
|
||||
struct devlink_resource *resource, u64 resource_id)
|
||||
__devlink_resource_find(struct list_head *resource_list_head,
|
||||
struct devlink_resource *resource,
|
||||
u64 resource_id)
|
||||
{
|
||||
struct list_head *resource_list;
|
||||
|
||||
if (resource)
|
||||
resource_list = &resource->resource_list;
|
||||
else
|
||||
resource_list = &devlink->resource_list;
|
||||
resource_list = resource_list_head;
|
||||
|
||||
list_for_each_entry(resource, resource_list, list) {
|
||||
struct devlink_resource *child_resource;
|
||||
@@ -52,14 +53,23 @@ devlink_resource_find(struct devlink *devlink,
|
||||
if (resource->id == resource_id)
|
||||
return resource;
|
||||
|
||||
child_resource = devlink_resource_find(devlink, resource,
|
||||
resource_id);
|
||||
child_resource = __devlink_resource_find(resource_list_head,
|
||||
resource,
|
||||
resource_id);
|
||||
if (child_resource)
|
||||
return child_resource;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct devlink_resource *
|
||||
devlink_resource_find(struct devlink *devlink,
|
||||
struct devlink_resource *resource, u64 resource_id)
|
||||
{
|
||||
return __devlink_resource_find(&devlink->resource_list,
|
||||
resource, resource_id);
|
||||
}
|
||||
|
||||
static void
|
||||
devlink_resource_validate_children(struct devlink_resource *resource)
|
||||
{
|
||||
@@ -213,11 +223,38 @@ static int devlink_resource_put(struct devlink *devlink, struct sk_buff *skb,
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
static int devlink_resource_list_fill(struct sk_buff *skb,
|
||||
struct devlink *devlink,
|
||||
struct list_head *resource_list_head,
|
||||
int *idx)
|
||||
{
|
||||
struct devlink_resource *resource;
|
||||
int i = 0;
|
||||
int err;
|
||||
|
||||
list_for_each_entry(resource, resource_list_head, list) {
|
||||
if (i < *idx) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
err = devlink_resource_put(devlink, skb, resource);
|
||||
if (err) {
|
||||
*idx = i;
|
||||
return err;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
*idx = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int devlink_resource_fill(struct genl_info *info,
|
||||
enum devlink_command cmd, int flags)
|
||||
{
|
||||
struct devlink_port *devlink_port = info->user_ptr[1];
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink_resource *resource;
|
||||
struct list_head *resource_list;
|
||||
struct nlattr *resources_attr;
|
||||
struct sk_buff *skb = NULL;
|
||||
struct nlmsghdr *nlh;
|
||||
@@ -226,7 +263,9 @@ static int devlink_resource_fill(struct genl_info *info,
|
||||
int i;
|
||||
int err;
|
||||
|
||||
resource = list_first_entry(&devlink->resource_list,
|
||||
resource_list = devlink_port ?
|
||||
&devlink_port->resource_list : &devlink->resource_list;
|
||||
resource = list_first_entry(resource_list,
|
||||
struct devlink_resource, list);
|
||||
start_again:
|
||||
err = devlink_nl_msg_reply_and_new(&skb, info);
|
||||
@@ -242,6 +281,9 @@ static int devlink_resource_fill(struct genl_info *info,
|
||||
|
||||
if (devlink_nl_put_handle(skb, devlink))
|
||||
goto nla_put_failure;
|
||||
if (devlink_port &&
|
||||
nla_put_u32(skb, DEVLINK_ATTR_PORT_INDEX, devlink_port->index))
|
||||
goto nla_put_failure;
|
||||
|
||||
resources_attr = nla_nest_start_noflag(skb,
|
||||
DEVLINK_ATTR_RESOURCE_LIST);
|
||||
@@ -250,7 +292,7 @@ static int devlink_resource_fill(struct genl_info *info,
|
||||
|
||||
incomplete = false;
|
||||
i = 0;
|
||||
list_for_each_entry_from(resource, &devlink->resource_list, list) {
|
||||
list_for_each_entry_from(resource, resource_list, list) {
|
||||
err = devlink_resource_put(devlink, skb, resource);
|
||||
if (err) {
|
||||
if (!i)
|
||||
@@ -284,14 +326,133 @@ static int devlink_resource_fill(struct genl_info *info,
|
||||
|
||||
int devlink_nl_resource_dump_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink_port *devlink_port = info->user_ptr[1];
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct list_head *resource_list;
|
||||
|
||||
if (list_empty(&devlink->resource_list))
|
||||
if (info->attrs[DEVLINK_ATTR_PORT_INDEX] && !devlink_port)
|
||||
return -ENODEV;
|
||||
|
||||
resource_list = devlink_port ?
|
||||
&devlink_port->resource_list : &devlink->resource_list;
|
||||
if (list_empty(resource_list))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return devlink_resource_fill(info, DEVLINK_CMD_RESOURCE_DUMP, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
devlink_resource_dump_fill_one(struct sk_buff *skb, struct devlink *devlink,
|
||||
struct devlink_port *devlink_port,
|
||||
struct netlink_callback *cb, int flags, int *idx)
|
||||
{
|
||||
struct list_head *resource_list;
|
||||
struct nlattr *resources_attr;
|
||||
int start_idx = *idx;
|
||||
void *hdr;
|
||||
int err;
|
||||
|
||||
resource_list = devlink_port ?
|
||||
&devlink_port->resource_list : &devlink->resource_list;
|
||||
|
||||
if (list_empty(resource_list))
|
||||
return 0;
|
||||
|
||||
err = -EMSGSIZE;
|
||||
hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
|
||||
&devlink_nl_family, flags, DEVLINK_CMD_RESOURCE_DUMP);
|
||||
if (!hdr)
|
||||
return err;
|
||||
|
||||
if (devlink_nl_put_handle(skb, devlink))
|
||||
goto nla_put_failure;
|
||||
if (devlink_port &&
|
||||
nla_put_u32(skb, DEVLINK_ATTR_PORT_INDEX, devlink_port->index))
|
||||
goto nla_put_failure;
|
||||
|
||||
resources_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_RESOURCE_LIST);
|
||||
if (!resources_attr)
|
||||
goto nla_put_failure;
|
||||
|
||||
err = devlink_resource_list_fill(skb, devlink, resource_list, idx);
|
||||
if (err) {
|
||||
if (*idx == start_idx)
|
||||
goto resource_list_cancel;
|
||||
nla_nest_end(skb, resources_attr);
|
||||
genlmsg_end(skb, hdr);
|
||||
return err;
|
||||
}
|
||||
nla_nest_end(skb, resources_attr);
|
||||
genlmsg_end(skb, hdr);
|
||||
return 0;
|
||||
|
||||
resource_list_cancel:
|
||||
nla_nest_cancel(skb, resources_attr);
|
||||
nla_put_failure:
|
||||
genlmsg_cancel(skb, hdr);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
devlink_nl_resource_dump_one(struct sk_buff *skb, struct devlink *devlink,
|
||||
struct netlink_callback *cb, int flags)
|
||||
{
|
||||
struct devlink_nl_dump_state *state = devlink_dump_state(cb);
|
||||
const struct genl_info *info = genl_info_dump(cb);
|
||||
struct devlink_port *devlink_port;
|
||||
struct nlattr *scope_attr = NULL;
|
||||
unsigned long port_idx;
|
||||
u32 scope = 0;
|
||||
int err;
|
||||
|
||||
if (info->attrs && info->attrs[DEVLINK_ATTR_RESOURCE_SCOPE_MASK]) {
|
||||
scope_attr = info->attrs[DEVLINK_ATTR_RESOURCE_SCOPE_MASK];
|
||||
scope = nla_get_u32(scope_attr);
|
||||
if (!scope) {
|
||||
NL_SET_ERR_MSG_ATTR(info->extack, scope_attr,
|
||||
"empty resource scope selection");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!state->port_ctx.index_valid &&
|
||||
(!scope || (scope & DEVLINK_RESOURCE_SCOPE_DEV))) {
|
||||
err = devlink_resource_dump_fill_one(skb, devlink, NULL,
|
||||
cb, flags, &state->idx);
|
||||
if (err)
|
||||
return err;
|
||||
state->idx = 0;
|
||||
}
|
||||
|
||||
if (scope && !(scope & DEVLINK_RESOURCE_SCOPE_PORT))
|
||||
goto out;
|
||||
/* Check in case port was removed between dump callbacks. */
|
||||
if (state->port_ctx.index_valid &&
|
||||
!xa_load(&devlink->ports, state->port_ctx.index))
|
||||
state->idx = 0;
|
||||
state->port_ctx.index_valid = true;
|
||||
xa_for_each_start(&devlink->ports, port_idx, devlink_port,
|
||||
state->port_ctx.index) {
|
||||
err = devlink_resource_dump_fill_one(skb, devlink, devlink_port,
|
||||
cb, flags, &state->idx);
|
||||
if (err) {
|
||||
state->port_ctx.index = port_idx;
|
||||
return err;
|
||||
}
|
||||
state->idx = 0;
|
||||
}
|
||||
out:
|
||||
state->port_ctx.index_valid = false;
|
||||
state->port_ctx.index = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int devlink_nl_resource_dump_dumpit(struct sk_buff *skb,
|
||||
struct netlink_callback *cb)
|
||||
{
|
||||
return devlink_nl_dumpit(skb, cb, devlink_nl_resource_dump_one);
|
||||
}
|
||||
|
||||
int devlink_resources_validate(struct devlink *devlink,
|
||||
struct devlink_resource *resource,
|
||||
struct genl_info *info)
|
||||
@@ -314,26 +475,12 @@ int devlink_resources_validate(struct devlink *devlink,
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* devl_resource_register - devlink resource register
|
||||
*
|
||||
* @devlink: devlink
|
||||
* @resource_name: resource's name
|
||||
* @resource_size: resource's size
|
||||
* @resource_id: resource's id
|
||||
* @parent_resource_id: resource's parent id
|
||||
* @size_params: size parameters
|
||||
*
|
||||
* Generic resources should reuse the same names across drivers.
|
||||
* Please see the generic resources list at:
|
||||
* Documentation/networking/devlink/devlink-resource.rst
|
||||
*/
|
||||
int devl_resource_register(struct devlink *devlink,
|
||||
const char *resource_name,
|
||||
u64 resource_size,
|
||||
u64 resource_id,
|
||||
u64 parent_resource_id,
|
||||
const struct devlink_resource_size_params *size_params)
|
||||
static int
|
||||
__devl_resource_register(struct devlink *devlink,
|
||||
struct list_head *resource_list_head,
|
||||
const char *resource_name, u64 resource_size,
|
||||
u64 resource_id, u64 parent_resource_id,
|
||||
const struct devlink_resource_size_params *params)
|
||||
{
|
||||
struct devlink_resource *resource;
|
||||
struct list_head *resource_list;
|
||||
@@ -343,7 +490,8 @@ int devl_resource_register(struct devlink *devlink,
|
||||
|
||||
top_hierarchy = parent_resource_id == DEVLINK_RESOURCE_ID_PARENT_TOP;
|
||||
|
||||
resource = devlink_resource_find(devlink, NULL, resource_id);
|
||||
resource = __devlink_resource_find(resource_list_head, NULL,
|
||||
resource_id);
|
||||
if (resource)
|
||||
return -EEXIST;
|
||||
|
||||
@@ -352,12 +500,13 @@ int devl_resource_register(struct devlink *devlink,
|
||||
return -ENOMEM;
|
||||
|
||||
if (top_hierarchy) {
|
||||
resource_list = &devlink->resource_list;
|
||||
resource_list = resource_list_head;
|
||||
} else {
|
||||
struct devlink_resource *parent_resource;
|
||||
|
||||
parent_resource = devlink_resource_find(devlink, NULL,
|
||||
parent_resource_id);
|
||||
parent_resource = __devlink_resource_find(resource_list_head,
|
||||
NULL,
|
||||
parent_resource_id);
|
||||
if (parent_resource) {
|
||||
resource_list = &parent_resource->resource_list;
|
||||
resource->parent = parent_resource;
|
||||
@@ -372,23 +521,64 @@ int devl_resource_register(struct devlink *devlink,
|
||||
resource->size_new = resource_size;
|
||||
resource->id = resource_id;
|
||||
resource->size_valid = true;
|
||||
memcpy(&resource->size_params, size_params,
|
||||
sizeof(resource->size_params));
|
||||
memcpy(&resource->size_params, params, sizeof(resource->size_params));
|
||||
INIT_LIST_HEAD(&resource->resource_list);
|
||||
list_add_tail(&resource->list, resource_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* devl_resource_register - devlink resource register
|
||||
*
|
||||
* @devlink: devlink
|
||||
* @resource_name: resource's name
|
||||
* @resource_size: resource's size
|
||||
* @resource_id: resource's id
|
||||
* @parent_resource_id: resource's parent id
|
||||
* @params: size parameters
|
||||
*
|
||||
* Generic resources should reuse the same names across drivers.
|
||||
* Please see the generic resources list at:
|
||||
* Documentation/networking/devlink/devlink-resource.rst
|
||||
*
|
||||
* Return: 0 on success, negative error code otherwise.
|
||||
*/
|
||||
int devl_resource_register(struct devlink *devlink, const char *resource_name,
|
||||
u64 resource_size, u64 resource_id,
|
||||
u64 parent_resource_id,
|
||||
const struct devlink_resource_size_params *params)
|
||||
{
|
||||
return __devl_resource_register(devlink, &devlink->resource_list,
|
||||
resource_name, resource_size,
|
||||
resource_id, parent_resource_id,
|
||||
params);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devl_resource_register);
|
||||
|
||||
static void devlink_resource_unregister(struct devlink *devlink,
|
||||
struct devlink_resource *resource)
|
||||
static void devlink_resource_unregister(struct devlink_resource *resource)
|
||||
{
|
||||
struct devlink_resource *tmp, *child_resource;
|
||||
|
||||
list_for_each_entry_safe(child_resource, tmp, &resource->resource_list,
|
||||
list) {
|
||||
devlink_resource_unregister(devlink, child_resource);
|
||||
devlink_resource_unregister(child_resource);
|
||||
list_del(&child_resource->list);
|
||||
kfree(child_resource);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
__devl_resources_unregister(struct devlink *devlink,
|
||||
struct list_head *resource_list_head)
|
||||
{
|
||||
struct devlink_resource *tmp, *child_resource;
|
||||
|
||||
lockdep_assert_held(&devlink->lock);
|
||||
|
||||
list_for_each_entry_safe(child_resource, tmp, resource_list_head,
|
||||
list) {
|
||||
devlink_resource_unregister(child_resource);
|
||||
list_del(&child_resource->list);
|
||||
kfree(child_resource);
|
||||
}
|
||||
@@ -401,16 +591,7 @@ static void devlink_resource_unregister(struct devlink *devlink,
|
||||
*/
|
||||
void devl_resources_unregister(struct devlink *devlink)
|
||||
{
|
||||
struct devlink_resource *tmp, *child_resource;
|
||||
|
||||
lockdep_assert_held(&devlink->lock);
|
||||
|
||||
list_for_each_entry_safe(child_resource, tmp, &devlink->resource_list,
|
||||
list) {
|
||||
devlink_resource_unregister(devlink, child_resource);
|
||||
list_del(&child_resource->list);
|
||||
kfree(child_resource);
|
||||
}
|
||||
__devl_resources_unregister(devlink, &devlink->resource_list);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devl_resources_unregister);
|
||||
|
||||
@@ -502,3 +683,46 @@ void devl_resource_occ_get_unregister(struct devlink *devlink,
|
||||
resource->occ_get_priv = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devl_resource_occ_get_unregister);
|
||||
|
||||
/**
|
||||
* devl_port_resource_register - devlink port resource register
|
||||
*
|
||||
* @devlink_port: devlink port
|
||||
* @resource_name: resource's name
|
||||
* @resource_size: resource's size
|
||||
* @resource_id: resource's id
|
||||
* @parent_resource_id: resource's parent id
|
||||
* @params: size parameters
|
||||
*
|
||||
* Generic resources should reuse the same names across drivers.
|
||||
* Please see the generic resources list at:
|
||||
* Documentation/networking/devlink/devlink-resource.rst
|
||||
*
|
||||
* Return: 0 on success, negative error code otherwise.
|
||||
*/
|
||||
int
|
||||
devl_port_resource_register(struct devlink_port *devlink_port,
|
||||
const char *resource_name,
|
||||
u64 resource_size, u64 resource_id,
|
||||
u64 parent_resource_id,
|
||||
const struct devlink_resource_size_params *params)
|
||||
{
|
||||
return __devl_resource_register(devlink_port->devlink,
|
||||
&devlink_port->resource_list,
|
||||
resource_name, resource_size,
|
||||
resource_id, parent_resource_id,
|
||||
params);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devl_port_resource_register);
|
||||
|
||||
/**
|
||||
* devl_port_resources_unregister - unregister all devlink port resources
|
||||
*
|
||||
* @devlink_port: devlink port
|
||||
*/
|
||||
void devl_port_resources_unregister(struct devlink_port *devlink_port)
|
||||
{
|
||||
__devl_resources_unregister(devlink_port->devlink,
|
||||
&devlink_port->resource_list);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devl_port_resources_unregister);
|
||||
|
||||
@@ -5,7 +5,8 @@ lib_dir=$(dirname $0)/../../../net/forwarding
|
||||
|
||||
ALL_TESTS="fw_flash_test params_test \
|
||||
params_default_test regions_test reload_test \
|
||||
netns_reload_test resource_test dev_info_test \
|
||||
netns_reload_test resource_test resource_dump_test \
|
||||
port_resource_doit_test dev_info_test \
|
||||
empty_reporter_test dummy_reporter_test rate_test"
|
||||
NUM_NETIFS=0
|
||||
source $lib_dir/lib.sh
|
||||
@@ -482,6 +483,56 @@ resource_test()
|
||||
log_test "resource test"
|
||||
}
|
||||
|
||||
resource_dump_test()
|
||||
{
|
||||
RET=0
|
||||
|
||||
local port_jq
|
||||
local dev_jq
|
||||
local dl_jq
|
||||
local count
|
||||
|
||||
dl_jq="with_entries(select(.key | startswith(\"$DL_HANDLE\")))"
|
||||
port_jq="[.[] | $dl_jq | keys |"
|
||||
port_jq+=" map(select(test(\"/.+/\"))) | length] | add"
|
||||
dev_jq="[.[] | $dl_jq | keys |"
|
||||
dev_jq+=" map(select(test(\"/.+/\")|not)) | length] | add"
|
||||
|
||||
if ! devlink resource help 2>&1 | grep -q "scope"; then
|
||||
echo "SKIP: devlink resource show not supported"
|
||||
return
|
||||
fi
|
||||
|
||||
devlink resource show > /dev/null 2>&1
|
||||
check_err $? "Failed to dump all resources"
|
||||
|
||||
count=$(cmd_jq "devlink resource show -j" "$port_jq")
|
||||
[ "$count" -gt "0" ]
|
||||
check_err $? "missing port resources in resource dump"
|
||||
|
||||
count=$(cmd_jq "devlink resource show -j" "$dev_jq")
|
||||
[ "$count" -gt "0" ]
|
||||
check_err $? "missing device resources in resource dump"
|
||||
|
||||
count=$(cmd_jq "devlink resource show scope dev -j" "$dev_jq")
|
||||
[ "$count" -gt "0" ]
|
||||
check_err $? "dev scope missing device resources"
|
||||
|
||||
count=$(cmd_jq "devlink resource show scope dev -j" "$port_jq")
|
||||
[ "$count" -eq "0" ]
|
||||
check_err $? "dev scope returned port resources"
|
||||
|
||||
count=$(cmd_jq "devlink resource show scope port -j" "$port_jq")
|
||||
[ "$count" -gt "0" ]
|
||||
check_err $? "port scope missing port resources"
|
||||
|
||||
count=$(cmd_jq "devlink resource show scope port -j" "$dev_jq")
|
||||
[ "$count" -eq "0" ]
|
||||
check_err $? "port scope returned device resources"
|
||||
|
||||
log_test "resource dump test"
|
||||
}
|
||||
|
||||
info_get()
|
||||
{
|
||||
local name=$1
|
||||
@@ -768,6 +819,32 @@ rate_node_del()
|
||||
devlink port function rate del $handle
|
||||
}
|
||||
|
||||
port_resource_doit_test()
|
||||
{
|
||||
RET=0
|
||||
|
||||
local port_handle="${DL_HANDLE}/0"
|
||||
local name
|
||||
local size
|
||||
|
||||
if ! devlink resource help 2>&1 | grep -q "PORT_INDEX"; then
|
||||
echo "SKIP: devlink resource show with port not supported"
|
||||
return
|
||||
fi
|
||||
|
||||
name=$(cmd_jq "devlink resource show $port_handle -j" \
|
||||
'.[][][].name')
|
||||
[ "$name" == "test_resource" ]
|
||||
check_err $? "wrong port resource name (got $name)"
|
||||
|
||||
size=$(cmd_jq "devlink resource show $port_handle -j" \
|
||||
'.[][][].size')
|
||||
[ "$size" == "20" ]
|
||||
check_err $? "wrong port resource size (got $size)"
|
||||
|
||||
log_test "port resource doit test"
|
||||
}
|
||||
|
||||
rate_test()
|
||||
{
|
||||
RET=0
|
||||
|
||||
Reference in New Issue
Block a user