devlink: Add parent dev to devlink API

Upcoming changes to the rate commands need the parent devlink specified.
This change adds a nested 'parent-dev' attribute to the API and helpers
to obtain and put a reference to the parent devlink instance in
info->ctx.

To avoid deadlocks, the parent devlink is unlocked before obtaining the
main devlink instance that is the target of the request.
A reference to the parent is kept until the end of the request to avoid
it suddenly disappearing.

This means that this reference is of limited use without additional
protection.

Signed-off-by: Cosmin Ratiu <cratiu@nvidia.com>
Reviewed-by: Carolina Jubran <cjubran@nvidia.com>
Reviewed-by: Jiri Pirko <jiri@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
Link: https://patch.msgid.link/20260701073254.754518-6-tariqt@nvidia.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Cosmin Ratiu 2026-07-01 10:32:45 +03:00 committed by Paolo Abeni
parent db078bc2b0
commit b5f90fd458
4 changed files with 56 additions and 5 deletions

View File

@ -895,6 +895,16 @@ attribute-sets:
resource-dump response. Bit 0 (dev) selects device-level resource-dump response. Bit 0 (dev) selects device-level
resources; bit 1 (port) selects port-level resources. resources; bit 1 (port) selects port-level resources.
When absent all classes are returned. When absent all classes are returned.
-
name: parent-dev
type: nest
nested-attributes: dl-parent-dev
doc: |
Identifies the devlink instance which owns the parent rate node.
Used with rate-set and rate-new to parent a rate object to a node on
a different devlink instance, enabling cross-device rate scheduling.
When absent, the parent node is resolved on the same instance.
- -
name: dl-dev-stats name: dl-dev-stats
subset-of: devlink subset-of: devlink
@ -1317,6 +1327,16 @@ attribute-sets:
Specifies the bandwidth share assigned to the Traffic Class. Specifies the bandwidth share assigned to the Traffic Class.
The bandwidth for the traffic class is determined The bandwidth for the traffic class is determined
in proportion to the sum of the shares of all configured classes. in proportion to the sum of the shares of all configured classes.
-
name: dl-parent-dev
subset-of: devlink
attributes:
-
name: bus-name
-
name: dev-name
-
name: index
operations: operations:
enum-model: directional enum-model: directional

View File

@ -648,6 +648,8 @@ enum devlink_attr {
DEVLINK_ATTR_INDEX, /* uint */ DEVLINK_ATTR_INDEX, /* uint */
DEVLINK_ATTR_RESOURCE_SCOPE_MASK, /* u32 */ DEVLINK_ATTR_RESOURCE_SCOPE_MASK, /* u32 */
DEVLINK_ATTR_PARENT_DEV, /* nested */
/* Add new attributes above here, update the spec in /* Add new attributes above here, update the spec in
* Documentation/netlink/specs/devlink.yaml and re-generate * Documentation/netlink/specs/devlink.yaml and re-generate
* net/devlink/netlink_gen.c. * net/devlink/netlink_gen.c.

View File

@ -154,6 +154,7 @@ int devlink_rel_devlink_handle_put(struct sk_buff *msg, struct devlink *devlink,
struct devlink_nl_ctx { struct devlink_nl_ctx {
struct devlink *devlink; struct devlink *devlink;
struct devlink_port *devlink_port; struct devlink_port *devlink_port;
struct devlink *parent_devlink;
}; };
static inline struct devlink_nl_ctx * static inline struct devlink_nl_ctx *
@ -197,6 +198,8 @@ typedef int devlink_nl_dump_one_func_t(struct sk_buff *msg,
struct devlink * struct devlink *
devlink_get_from_attrs_lock(struct net *net, struct nlattr **attrs, devlink_get_from_attrs_lock(struct net *net, struct nlattr **attrs,
bool dev_lock); bool dev_lock);
struct devlink *
devlink_get_parent_from_attrs_lock(struct net *net, struct nlattr **attrs);
int devlink_nl_dumpit(struct sk_buff *msg, struct netlink_callback *cb, int devlink_nl_dumpit(struct sk_buff *msg, struct netlink_callback *cb,
devlink_nl_dump_one_func_t *dump_one); devlink_nl_dump_one_func_t *dump_one);

View File

@ -12,6 +12,7 @@
#define DEVLINK_NL_FLAG_NEED_PORT BIT(0) #define DEVLINK_NL_FLAG_NEED_PORT BIT(0)
#define DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT BIT(1) #define DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT BIT(1)
#define DEVLINK_NL_FLAG_NEED_DEV_LOCK BIT(2) #define DEVLINK_NL_FLAG_NEED_DEV_LOCK BIT(2)
#define DEVLINK_NL_FLAG_OPTIONAL_PARENT_DEV BIT(3)
static const struct genl_multicast_group devlink_nl_mcgrps[] = { static const struct genl_multicast_group devlink_nl_mcgrps[] = {
[DEVLINK_MCGRP_CONFIG] = { .name = DEVLINK_GENL_MCGRP_CONFIG_NAME }, [DEVLINK_MCGRP_CONFIG] = { .name = DEVLINK_GENL_MCGRP_CONFIG_NAME },
@ -239,19 +240,39 @@ devlink_get_from_attrs_lock(struct net *net, struct nlattr **attrs,
return ERR_PTR(-ENODEV); return ERR_PTR(-ENODEV);
} }
struct devlink *
devlink_get_parent_from_attrs_lock(struct net *net, struct nlattr **attrs)
{
return ERR_PTR(-EOPNOTSUPP);
}
static int __devlink_nl_pre_doit(struct sk_buff *skb, struct genl_info *info, static int __devlink_nl_pre_doit(struct sk_buff *skb, struct genl_info *info,
u8 flags) u8 flags)
{ {
bool parent_dev = flags & DEVLINK_NL_FLAG_OPTIONAL_PARENT_DEV;
bool dev_lock = flags & DEVLINK_NL_FLAG_NEED_DEV_LOCK; bool dev_lock = flags & DEVLINK_NL_FLAG_NEED_DEV_LOCK;
struct devlink *devlink, *parent_devlink = NULL;
struct net *net = genl_info_net(info);
struct nlattr **attrs = info->attrs;
struct devlink_port *devlink_port; struct devlink_port *devlink_port;
struct devlink *devlink;
int err; int err;
devlink = devlink_get_from_attrs_lock(genl_info_net(info), info->attrs, if (parent_dev && attrs[DEVLINK_ATTR_PARENT_DEV]) {
dev_lock); parent_devlink = devlink_get_parent_from_attrs_lock(net, attrs);
if (IS_ERR(devlink)) if (IS_ERR(parent_devlink))
return PTR_ERR(devlink); return PTR_ERR(parent_devlink);
devlink_nl_ctx(info)->parent_devlink = parent_devlink;
/* Drop the parent devlink lock but don't release the reference.
* This will keep it alive until the end of the request.
*/
devl_unlock(parent_devlink);
}
devlink = devlink_get_from_attrs_lock(net, attrs, dev_lock);
if (IS_ERR(devlink)) {
err = PTR_ERR(devlink);
goto parent_put;
}
devlink_nl_ctx(info)->devlink = devlink; devlink_nl_ctx(info)->devlink = devlink;
if (flags & DEVLINK_NL_FLAG_NEED_PORT) { if (flags & DEVLINK_NL_FLAG_NEED_PORT) {
devlink_port = devlink_port_get_from_info(devlink, info); devlink_port = devlink_port_get_from_info(devlink, info);
@ -270,6 +291,9 @@ static int __devlink_nl_pre_doit(struct sk_buff *skb, struct genl_info *info,
unlock: unlock:
devl_dev_unlock(devlink, dev_lock); devl_dev_unlock(devlink, dev_lock);
devlink_put(devlink); devlink_put(devlink);
parent_put:
if (parent_dev && parent_devlink)
devlink_put(parent_devlink);
return err; return err;
} }
@ -307,6 +331,8 @@ static void __devlink_nl_post_doit(struct sk_buff *skb, struct genl_info *info,
devlink = devlink_nl_ctx(info)->devlink; devlink = devlink_nl_ctx(info)->devlink;
devl_dev_unlock(devlink, dev_lock); devl_dev_unlock(devlink, dev_lock);
devlink_put(devlink); devlink_put(devlink);
if (devlink_nl_ctx(info)->parent_devlink)
devlink_put(devlink_nl_ctx(info)->parent_devlink);
} }
void devlink_nl_post_doit(const struct genl_split_ops *ops, void devlink_nl_post_doit(const struct genl_split_ops *ops,