summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Kicinski <kuba@kernel.org>2026-04-08 19:55:43 -0700
committerJakub Kicinski <kuba@kernel.org>2026-04-08 19:55:43 -0700
commit58dd34dbd5b09749f33337296e76db54b2274bcc (patch)
tree95271e97d5407a87f14d18bac0b7f5a45e97eb9f
parent5ae4ba98d72509a4da592751be4d6b4dbfa8cac8 (diff)
parent78c327c1728de377020672d429250c80a8a13723 (diff)
downloadlwn-58dd34dbd5b09749f33337296e76db54b2274bcc.tar.gz
lwn-58dd34dbd5b09749f33337296e76db54b2274bcc.zip
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>
-rw-r--r--Documentation/netlink/specs/devlink.yaml32
-rw-r--r--Documentation/networking/devlink/devlink-resource.rst70
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/devlink.h4
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c37
-rw-r--r--drivers/net/netdevsim/dev.c23
-rw-r--r--drivers/net/netdevsim/netdevsim.h4
-rw-r--r--include/net/devlink.h10
-rw-r--r--include/uapi/linux/devlink.h11
-rw-r--r--net/devlink/devl_internal.h5
-rw-r--r--net/devlink/netlink.c2
-rw-r--r--net/devlink/netlink_gen.c24
-rw-r--r--net/devlink/netlink_gen.h8
-rw-r--r--net/devlink/port.c2
-rw-r--r--net/devlink/resource.c314
-rwxr-xr-xtools/testing/selftests/drivers/net/netdevsim/devlink.sh79
15 files changed, 568 insertions, 57 deletions
diff --git a/Documentation/netlink/specs/devlink.yaml b/Documentation/netlink/specs/devlink.yaml
index b495d56b9137..247b147d689f 100644
--- a/Documentation/netlink/specs/devlink.yaml
+++ b/Documentation/netlink/specs/devlink.yaml
@@ -159,6 +159,14 @@ definitions:
name: entry
-
type: enum
+ name: resource-scope
+ entries:
+ -
+ name: dev
+ -
+ name: port
+ -
+ type: enum
name: reload-action
entries:
-
@@ -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
diff --git a/Documentation/networking/devlink/devlink-resource.rst b/Documentation/networking/devlink/devlink-resource.rst
index 3d5ae51e65a2..47eec8f875b4 100644
--- a/Documentation/networking/devlink/devlink-resource.rst
+++ b/Documentation/networking/devlink/devlink-resource.rst
@@ -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.
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/devlink.h b/drivers/net/ethernet/mellanox/mlx5/core/devlink.h
index 43b9bf8829cf..4fbb3926a3e5 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/devlink.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/devlink.h
@@ -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,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c
index 8bffba85f21f..e1d11326af1b 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c
@@ -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);
diff --git a/drivers/net/netdevsim/dev.c b/drivers/net/netdevsim/dev.c
index e82de0fd3157..1e06e781c835 100644
--- a/drivers/net/netdevsim/dev.c
+++ b/drivers/net/netdevsim/dev.c
@@ -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 @@ err_nsim_destroy:
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);
}
diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h
index c904e14f6b3f..c7de53706ec4 100644
--- a/drivers/net/netdevsim/netdevsim.h
+++ b/drivers/net/netdevsim/netdevsim.h
@@ -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;
diff --git a/include/net/devlink.h b/include/net/devlink.h
index 3038af6ec017..bcd31de1f890 100644
--- a/include/net/devlink.h
+++ b/include/net/devlink.h
@@ -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);
diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h
index 7de2d8cc862f..0b165eac7619 100644
--- a/include/uapi/linux/devlink.h
+++ b/include/uapi/linux/devlink.h
@@ -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,
diff --git a/net/devlink/devl_internal.h b/net/devlink/devl_internal.h
index 7dfb7cdd2d23..e4e48ee2da5a 100644
--- a/net/devlink/devl_internal.h
+++ b/net/devlink/devl_internal.h
@@ -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;
};
};
diff --git a/net/devlink/netlink.c b/net/devlink/netlink.c
index 32ddbe244cb7..ae4afc739678 100644
--- a/net/devlink/netlink.c
+++ b/net/devlink/netlink.c
@@ -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)
diff --git a/net/devlink/netlink_gen.c b/net/devlink/netlink_gen.c
index eb35e80e01d1..81899786fd98 100644
--- a/net/devlink/netlink_gen.c
+++ b/net/devlink/netlink_gen.c
@@ -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,14 +964,21 @@ 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,
.pre_doit = devlink_nl_pre_doit_dev_lock,
diff --git a/net/devlink/netlink_gen.h b/net/devlink/netlink_gen.h
index 2817d53a0eba..20034b0929a8 100644
--- a/net/devlink/netlink_gen.h
+++ b/net/devlink/netlink_gen.h
@@ -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,
diff --git a/net/devlink/port.c b/net/devlink/port.c
index 7fcd1d3ed44c..485029d43428 100644
--- a/net/devlink/port.c
+++ b/net/devlink/port.c
@@ -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);
diff --git a/net/devlink/resource.c b/net/devlink/resource.c
index 351835a710b1..3d2f42bc2fb5 100644
--- a/net/devlink/resource.c
+++ b/net/devlink/resource.c
@@ -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 @@ nla_put_failure:
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 @@ start_again:
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 @@ start_again:
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 @@ err_resource_put:
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 (info->attrs[DEVLINK_ATTR_PORT_INDEX] && !devlink_port)
+ return -ENODEV;
- if (list_empty(&devlink->resource_list))
+ 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,46 +521,78 @@ 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);
}
}
-/**
- * devl_resources_unregister - free all resources
- *
- * @devlink: devlink
- */
-void devl_resources_unregister(struct devlink *devlink)
+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, &devlink->resource_list,
+ list_for_each_entry_safe(child_resource, tmp, resource_list_head,
list) {
- devlink_resource_unregister(devlink, child_resource);
+ devlink_resource_unregister(child_resource);
list_del(&child_resource->list);
kfree(child_resource);
}
}
+
+/**
+ * devl_resources_unregister - free all resources
+ *
+ * @devlink: devlink
+ */
+void devl_resources_unregister(struct devlink *devlink)
+{
+ __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);
diff --git a/tools/testing/selftests/drivers/net/netdevsim/devlink.sh b/tools/testing/selftests/drivers/net/netdevsim/devlink.sh
index 1b529ccaf050..22a626c6cde3 100755
--- a/tools/testing/selftests/drivers/net/netdevsim/devlink.sh
+++ b/tools/testing/selftests/drivers/net/netdevsim/devlink.sh
@@ -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