From 0061b5199d7c81076181a64529f7a799ebb89399 Mon Sep 17 00:00:00 2001 From: Cosmin Ratiu Date: Wed, 28 Jan 2026 13:25:33 +0200 Subject: devlink: Reverse locking order for nested instances Commit [1] defined the locking expectations for nested devlink instances: the nested-in devlink instance lock needs to be acquired before the nested devlink instance lock. The code handling devlink rels was architected with that assumption in mind. There are no actual users of double locking yet but that is about to change in the upcoming patches in the series. Code operating on nested devlink instances will require also obtaining the nested-in instance lock, but such code may already be called from a variety of places with the nested devlink instance lock. Then, there's no way to acquire the nested-in lock other than making sure that all callers acquire it first. Reversing the nested lock order allows incrementally acquiring the nested-in instance lock when needed (perhaps even a chain of locks up to the root) without affecting any caller. The only affected use of nesting is devlink_nl_nested_fill(), which iterates over nested devlink instances with the RCU lock, without locking them, so there's no possibility of deadlock. So this commit just updates a comment regarding the nested locks. [1] commit c137743bce02b ("devlink: introduce object and nested devlink relationship infra") Signed-off-by: Cosmin Ratiu Reviewed-by: Jiri Pirko Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260128112544.1661250-4-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- net/devlink/core.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'net/devlink') diff --git a/net/devlink/core.c b/net/devlink/core.c index 58093f49c090..6ae62c7f2a80 100644 --- a/net/devlink/core.c +++ b/net/devlink/core.c @@ -178,9 +178,7 @@ int devlink_rel_nested_in_add(u32 *rel_index, u32 devlink_index, * a notification of a change of this object should be sent * over netlink. The parent devlink instance lock needs to be * taken during the notification preparation. - * However, since the devlink lock of nested instance is held here, - * we would end with wrong devlink instance lock ordering and - * deadlock. Therefore the work is utilized to avoid that. + * Since the parent may or may not be locked, 'work' is utilized. */ void devlink_rel_nested_in_notify(struct devlink *devlink) { -- cgit v1.2.3 From 29903edf04c25e63ba1475801fb7038d76c5054b Mon Sep 17 00:00:00 2001 From: Cosmin Ratiu Date: Wed, 28 Jan 2026 13:25:35 +0200 Subject: devlink: Refactor devlink_rate_nodes_check devlink_rate_nodes_check() was used to verify there are no devlink rate nodes created when switching the esw mode. Rate management code is about to become more complex, so refactor this function: - remove unused param 'mode'. - add a new 'rate_filter' param. - rename to devlink_rates_check(). - expose devlink_rate_is_node() to be used as a rate filter. This makes it more usable from multiple places, so use it from those places as well. Signed-off-by: Cosmin Ratiu Reviewed-by: Carolina Jubran Reviewed-by: Jiri Pirko Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260128112544.1661250-6-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- net/devlink/core.c | 2 +- net/devlink/dev.c | 7 ++++--- net/devlink/devl_internal.h | 6 ++++-- net/devlink/rate.c | 13 +++++++------ 4 files changed, 16 insertions(+), 12 deletions(-) (limited to 'net/devlink') diff --git a/net/devlink/core.c b/net/devlink/core.c index 6ae62c7f2a80..da56e2b8afc1 100644 --- a/net/devlink/core.c +++ b/net/devlink/core.c @@ -475,7 +475,7 @@ void devlink_free(struct devlink *devlink) WARN_ON(!list_empty(&devlink->resource_list)); WARN_ON(!list_empty(&devlink->dpipe_table_list)); WARN_ON(!list_empty(&devlink->sb_list)); - WARN_ON(!list_empty(&devlink->rate_list)); + WARN_ON(devlink_rates_check(devlink, NULL, NULL)); WARN_ON(!list_empty(&devlink->linecard_list)); WARN_ON(!xa_empty(&devlink->ports)); diff --git a/net/devlink/dev.c b/net/devlink/dev.c index 02602704bdea..e3a36de4f4ae 100644 --- a/net/devlink/dev.c +++ b/net/devlink/dev.c @@ -434,7 +434,7 @@ static void devlink_reload_reinit_sanity_check(struct devlink *devlink) WARN_ON(!list_empty(&devlink->trap_list)); WARN_ON(!list_empty(&devlink->dpipe_table_list)); WARN_ON(!list_empty(&devlink->sb_list)); - WARN_ON(!list_empty(&devlink->rate_list)); + WARN_ON(devlink_rates_check(devlink, NULL, NULL)); WARN_ON(!list_empty(&devlink->linecard_list)); WARN_ON(!xa_empty(&devlink->ports)); } @@ -713,10 +713,11 @@ int devlink_nl_eswitch_set_doit(struct sk_buff *skb, struct genl_info *info) if (info->attrs[DEVLINK_ATTR_ESWITCH_MODE]) { if (!ops->eswitch_mode_set) return -EOPNOTSUPP; - mode = nla_get_u16(info->attrs[DEVLINK_ATTR_ESWITCH_MODE]); - err = devlink_rate_nodes_check(devlink, mode, info->extack); + err = devlink_rates_check(devlink, devlink_rate_is_node, + info->extack); if (err) return err; + mode = nla_get_u16(info->attrs[DEVLINK_ATTR_ESWITCH_MODE]); err = ops->eswitch_mode_set(devlink, mode, info->extack); if (err) return err; diff --git a/net/devlink/devl_internal.h b/net/devlink/devl_internal.h index 14eaad9cfe35..1377864383bc 100644 --- a/net/devlink/devl_internal.h +++ b/net/devlink/devl_internal.h @@ -297,8 +297,10 @@ int devlink_resources_validate(struct devlink *devlink, struct genl_info *info); /* Rates */ -int devlink_rate_nodes_check(struct devlink *devlink, u16 mode, - struct netlink_ext_ack *extack); +bool devlink_rate_is_node(const struct devlink_rate *devlink_rate); +int devlink_rates_check(struct devlink *devlink, + bool (*rate_filter)(const struct devlink_rate *), + struct netlink_ext_ack *extack); /* Linecards */ unsigned int devlink_linecard_index(struct devlink_linecard *linecard); diff --git a/net/devlink/rate.c b/net/devlink/rate.c index d157a8419bca..0d68b5c477dc 100644 --- a/net/devlink/rate.c +++ b/net/devlink/rate.c @@ -12,8 +12,7 @@ devlink_rate_is_leaf(struct devlink_rate *devlink_rate) return devlink_rate->type == DEVLINK_RATE_TYPE_LEAF; } -static inline bool -devlink_rate_is_node(struct devlink_rate *devlink_rate) +bool devlink_rate_is_node(const struct devlink_rate *devlink_rate) { return devlink_rate->type == DEVLINK_RATE_TYPE_NODE; } @@ -688,14 +687,16 @@ int devlink_nl_rate_del_doit(struct sk_buff *skb, struct genl_info *info) return err; } -int devlink_rate_nodes_check(struct devlink *devlink, u16 mode, - struct netlink_ext_ack *extack) +int devlink_rates_check(struct devlink *devlink, + bool (*rate_filter)(const struct devlink_rate *), + struct netlink_ext_ack *extack) { struct devlink_rate *devlink_rate; list_for_each_entry(devlink_rate, &devlink->rate_list, list) - if (devlink_rate_is_node(devlink_rate)) { - NL_SET_ERR_MSG(extack, "Rate node(s) exists."); + if (!rate_filter || rate_filter(devlink_rate)) { + if (extack) + NL_SET_ERR_MSG(extack, "Rate node(s) exists."); return -EBUSY; } return 0; -- cgit v1.2.3