diff options
Diffstat (limited to 'net/devlink/netlink.c')
| -rw-r--r-- | net/devlink/netlink.c | 82 |
1 files changed, 73 insertions, 9 deletions
diff --git a/net/devlink/netlink.c b/net/devlink/netlink.c index ae4afc739678..300580c1a217 100644 --- a/net/devlink/netlink.c +++ b/net/devlink/netlink.c @@ -12,6 +12,7 @@ #define DEVLINK_NL_FLAG_NEED_PORT BIT(0) #define DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT BIT(1) #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[] = { [DEVLINK_MCGRP_CONFIG] = { .name = DEVLINK_GENL_MCGRP_CONFIG_NAME }, @@ -239,37 +240,82 @@ found: return ERR_PTR(-ENODEV); } +struct devlink * +devlink_get_parent_from_attrs_lock(struct net *net, struct nlattr **attrs) +{ + unsigned int maxtype = ARRAY_SIZE(devlink_dl_parent_dev_nl_policy) - 1; + struct devlink *devlink; + struct nlattr **tb; + int err; + + if (!attrs[DEVLINK_ATTR_PARENT_DEV]) + return ERR_PTR(-EINVAL); + + tb = kcalloc(maxtype + 1, sizeof(*tb), GFP_KERNEL); + if (!tb) + return ERR_PTR(-ENOMEM); + + err = nla_parse_nested(tb, maxtype, attrs[DEVLINK_ATTR_PARENT_DEV], + devlink_dl_parent_dev_nl_policy, NULL); + if (err) + goto out; + + devlink = devlink_get_from_attrs_lock(net, tb, false); + kfree(tb); + return devlink; +out: + kfree(tb); + return ERR_PTR(err); +} + static int __devlink_nl_pre_doit(struct sk_buff *skb, struct genl_info *info, u8 flags) { + bool parent_dev = flags & DEVLINK_NL_FLAG_OPTIONAL_PARENT_DEV; 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 *devlink; int err; - devlink = devlink_get_from_attrs_lock(genl_info_net(info), info->attrs, - dev_lock); - if (IS_ERR(devlink)) - return PTR_ERR(devlink); + if (parent_dev && attrs[DEVLINK_ATTR_PARENT_DEV]) { + parent_devlink = devlink_get_parent_from_attrs_lock(net, attrs); + if (IS_ERR(parent_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); + } - info->user_ptr[0] = 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; if (flags & DEVLINK_NL_FLAG_NEED_PORT) { devlink_port = devlink_port_get_from_info(devlink, info); if (IS_ERR(devlink_port)) { err = PTR_ERR(devlink_port); goto unlock; } - info->user_ptr[1] = devlink_port; + devlink_nl_ctx(info)->devlink_port = devlink_port; } else if (flags & DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT) { devlink_port = devlink_port_get_from_info(devlink, info); if (!IS_ERR(devlink_port)) - info->user_ptr[1] = devlink_port; + devlink_nl_ctx(info)->devlink_port = devlink_port; } return 0; unlock: devl_dev_unlock(devlink, dev_lock); devlink_put(devlink); +parent_put: + if (parent_dev && parent_devlink) + devlink_put(parent_devlink); return err; } @@ -298,15 +344,25 @@ int devlink_nl_pre_doit_port_optional(const struct genl_split_ops *ops, return __devlink_nl_pre_doit(skb, info, DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT); } +int devlink_nl_pre_doit_parent_dev_optional(const struct genl_split_ops *ops, + struct sk_buff *skb, + struct genl_info *info) +{ + return __devlink_nl_pre_doit(skb, info, + DEVLINK_NL_FLAG_OPTIONAL_PARENT_DEV); +} + static void __devlink_nl_post_doit(struct sk_buff *skb, struct genl_info *info, u8 flags) { bool dev_lock = flags & DEVLINK_NL_FLAG_NEED_DEV_LOCK; struct devlink *devlink; - devlink = info->user_ptr[0]; + devlink = devlink_nl_ctx(info)->devlink; devl_dev_unlock(devlink, dev_lock); 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, @@ -322,6 +378,14 @@ devlink_nl_post_doit_dev_lock(const struct genl_split_ops *ops, __devlink_nl_post_doit(skb, info, DEVLINK_NL_FLAG_NEED_DEV_LOCK); } +void +devlink_nl_post_doit_parent_dev_optional(const struct genl_split_ops *ops, + struct sk_buff *skb, + struct genl_info *info) +{ + __devlink_nl_post_doit(skb, info, DEVLINK_NL_FLAG_OPTIONAL_PARENT_DEV); +} + static int devlink_nl_inst_single_dumpit(struct sk_buff *msg, struct netlink_callback *cb, int flags, devlink_nl_dump_one_func_t *dump_one, |
