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:
Jakub Kicinski
2026-04-08 19:55:43 -07:00
15 changed files with 570 additions and 59 deletions

View File

@@ -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

View File

@@ -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.

View File

@@ -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,

View File

@@ -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);

View File

@@ -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, &params);
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);
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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,

View File

@@ -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;
};
};

View File

@@ -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)

View File

@@ -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,

View File

@@ -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,

View File

@@ -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);

View File

@@ -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);

View File

@@ -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