net: devlink: allow to change namespaces during reload
All devlink instances are created in init_net and stay there for a lifetime. Allow user to be able to move devlink instances into namespaces during devlink reload operation. That ensures proper re-instantiation of driver objects, including netdevices. Signed-off-by: Jiri Pirko <jiri@mellanox.com> Acked-by: Jakub Kicinski <jakub.kicinski@netronome.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
4f174bbcc9
commit
070c63f20f
|
@ -3935,13 +3935,17 @@ static void mlx4_restart_one_down(struct pci_dev *pdev);
|
|||
static int mlx4_restart_one_up(struct pci_dev *pdev, bool reload,
|
||||
struct devlink *devlink);
|
||||
|
||||
static int mlx4_devlink_reload_down(struct devlink *devlink,
|
||||
static int mlx4_devlink_reload_down(struct devlink *devlink, bool netns_change,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct mlx4_priv *priv = devlink_priv(devlink);
|
||||
struct mlx4_dev *dev = &priv->dev;
|
||||
struct mlx4_dev_persistent *persist = dev->persist;
|
||||
|
||||
if (netns_change) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "Namespace change is not supported");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
if (persist->num_vfs)
|
||||
mlx4_warn(persist->dev, "Reload performed on PF, will cause reset on operating Virtual Functions\n");
|
||||
mlx4_restart_one_down(persist->pdev);
|
||||
|
|
|
@ -985,6 +985,7 @@ mlxsw_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
|
|||
|
||||
static int
|
||||
mlxsw_devlink_core_bus_device_reload_down(struct devlink *devlink,
|
||||
bool netns_change,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
|
||||
|
|
|
@ -473,7 +473,7 @@ static int nsim_dev_reload_create(struct nsim_dev *nsim_dev,
|
|||
struct netlink_ext_ack *extack);
|
||||
static void nsim_dev_reload_destroy(struct nsim_dev *nsim_dev);
|
||||
|
||||
static int nsim_dev_reload_down(struct devlink *devlink,
|
||||
static int nsim_dev_reload_down(struct devlink *devlink, bool netns_change,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct nsim_dev *nsim_dev = devlink_priv(devlink);
|
||||
|
|
|
@ -643,7 +643,7 @@ enum devlink_trap_group_generic_id {
|
|||
}
|
||||
|
||||
struct devlink_ops {
|
||||
int (*reload_down)(struct devlink *devlink,
|
||||
int (*reload_down)(struct devlink *devlink, bool netns_change,
|
||||
struct netlink_ext_ack *extack);
|
||||
int (*reload_up)(struct devlink *devlink,
|
||||
struct netlink_ext_ack *extack);
|
||||
|
|
|
@ -421,6 +421,10 @@ enum devlink_attr {
|
|||
|
||||
DEVLINK_ATTR_RELOAD_FAILED, /* u8 0 or 1 */
|
||||
|
||||
DEVLINK_ATTR_NETNS_FD, /* u32 */
|
||||
DEVLINK_ATTR_NETNS_PID, /* u32 */
|
||||
DEVLINK_ATTR_NETNS_ID, /* u32 */
|
||||
|
||||
/* add new attributes above here, update the policy in devlink.c */
|
||||
|
||||
__DEVLINK_ATTR_MAX,
|
||||
|
|
|
@ -435,8 +435,16 @@ static void devlink_nl_post_doit(const struct genl_ops *ops,
|
|||
{
|
||||
struct devlink *devlink;
|
||||
|
||||
/* When devlink changes netns, it would not be found
|
||||
* by devlink_get_from_info(). So try if it is stored first.
|
||||
*/
|
||||
if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_DEVLINK) {
|
||||
devlink = info->user_ptr[0];
|
||||
} else {
|
||||
devlink = devlink_get_from_info(info);
|
||||
if (~ops->internal_flags & DEVLINK_NL_FLAG_NO_LOCK)
|
||||
WARN_ON(IS_ERR(devlink));
|
||||
}
|
||||
if (!IS_ERR(devlink) && ~ops->internal_flags & DEVLINK_NL_FLAG_NO_LOCK)
|
||||
mutex_unlock(&devlink->lock);
|
||||
mutex_unlock(&devlink_mutex);
|
||||
}
|
||||
|
@ -2675,6 +2683,72 @@ devlink_resources_validate(struct devlink *devlink,
|
|||
return err;
|
||||
}
|
||||
|
||||
static struct net *devlink_netns_get(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct nlattr *netns_pid_attr = info->attrs[DEVLINK_ATTR_NETNS_PID];
|
||||
struct nlattr *netns_fd_attr = info->attrs[DEVLINK_ATTR_NETNS_FD];
|
||||
struct nlattr *netns_id_attr = info->attrs[DEVLINK_ATTR_NETNS_ID];
|
||||
struct net *net;
|
||||
|
||||
if (!!netns_pid_attr + !!netns_fd_attr + !!netns_id_attr > 1) {
|
||||
NL_SET_ERR_MSG(info->extack, "multiple netns identifying attributes specified");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
if (netns_pid_attr) {
|
||||
net = get_net_ns_by_pid(nla_get_u32(netns_pid_attr));
|
||||
} else if (netns_fd_attr) {
|
||||
net = get_net_ns_by_fd(nla_get_u32(netns_fd_attr));
|
||||
} else if (netns_id_attr) {
|
||||
net = get_net_ns_by_id(sock_net(skb->sk),
|
||||
nla_get_u32(netns_id_attr));
|
||||
if (!net)
|
||||
net = ERR_PTR(-EINVAL);
|
||||
} else {
|
||||
WARN_ON(1);
|
||||
net = ERR_PTR(-EINVAL);
|
||||
}
|
||||
if (IS_ERR(net)) {
|
||||
NL_SET_ERR_MSG(info->extack, "Unknown network namespace");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) {
|
||||
put_net(net);
|
||||
return ERR_PTR(-EPERM);
|
||||
}
|
||||
return net;
|
||||
}
|
||||
|
||||
static void devlink_param_notify(struct devlink *devlink,
|
||||
unsigned int port_index,
|
||||
struct devlink_param_item *param_item,
|
||||
enum devlink_command cmd);
|
||||
|
||||
static void devlink_reload_netns_change(struct devlink *devlink,
|
||||
struct net *dest_net)
|
||||
{
|
||||
struct devlink_param_item *param_item;
|
||||
|
||||
/* Userspace needs to be notified about devlink objects
|
||||
* removed from original and entering new network namespace.
|
||||
* The rest of the devlink objects are re-created during
|
||||
* reload process so the notifications are generated separatelly.
|
||||
*/
|
||||
|
||||
list_for_each_entry(param_item, &devlink->param_list, list)
|
||||
devlink_param_notify(devlink, 0, param_item,
|
||||
DEVLINK_CMD_PARAM_DEL);
|
||||
devlink_notify(devlink, DEVLINK_CMD_DEL);
|
||||
|
||||
devlink_net_set(devlink, dest_net);
|
||||
|
||||
devlink_notify(devlink, DEVLINK_CMD_NEW);
|
||||
list_for_each_entry(param_item, &devlink->param_list, list)
|
||||
devlink_param_notify(devlink, 0, param_item,
|
||||
DEVLINK_CMD_PARAM_NEW);
|
||||
}
|
||||
|
||||
static bool devlink_reload_supported(struct devlink *devlink)
|
||||
{
|
||||
return devlink->ops->reload_down && devlink->ops->reload_up;
|
||||
|
@ -2695,9 +2769,27 @@ bool devlink_is_reload_failed(const struct devlink *devlink)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(devlink_is_reload_failed);
|
||||
|
||||
static int devlink_reload(struct devlink *devlink, struct net *dest_net,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = devlink->ops->reload_down(devlink, !!dest_net, extack);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (dest_net && !net_eq(dest_net, devlink_net(devlink)))
|
||||
devlink_reload_netns_change(devlink, dest_net);
|
||||
|
||||
err = devlink->ops->reload_up(devlink, extack);
|
||||
devlink_reload_failed_set(devlink, !!err);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int devlink_nl_cmd_reload(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct net *dest_net = NULL;
|
||||
int err;
|
||||
|
||||
if (!devlink_reload_supported(devlink))
|
||||
|
@ -2708,11 +2800,20 @@ static int devlink_nl_cmd_reload(struct sk_buff *skb, struct genl_info *info)
|
|||
NL_SET_ERR_MSG_MOD(info->extack, "resources size validation failed");
|
||||
return err;
|
||||
}
|
||||
err = devlink->ops->reload_down(devlink, info->extack);
|
||||
if (err)
|
||||
return err;
|
||||
err = devlink->ops->reload_up(devlink, info->extack);
|
||||
devlink_reload_failed_set(devlink, !!err);
|
||||
|
||||
if (info->attrs[DEVLINK_ATTR_NETNS_PID] ||
|
||||
info->attrs[DEVLINK_ATTR_NETNS_FD] ||
|
||||
info->attrs[DEVLINK_ATTR_NETNS_ID]) {
|
||||
dest_net = devlink_netns_get(skb, info);
|
||||
if (IS_ERR(dest_net))
|
||||
return PTR_ERR(dest_net);
|
||||
}
|
||||
|
||||
err = devlink_reload(devlink, dest_net, info->extack);
|
||||
|
||||
if (dest_net)
|
||||
put_net(dest_net);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -5794,6 +5895,9 @@ static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {
|
|||
[DEVLINK_ATTR_TRAP_NAME] = { .type = NLA_NUL_STRING },
|
||||
[DEVLINK_ATTR_TRAP_ACTION] = { .type = NLA_U8 },
|
||||
[DEVLINK_ATTR_TRAP_GROUP_NAME] = { .type = NLA_NUL_STRING },
|
||||
[DEVLINK_ATTR_NETNS_PID] = { .type = NLA_U32 },
|
||||
[DEVLINK_ATTR_NETNS_FD] = { .type = NLA_U32 },
|
||||
[DEVLINK_ATTR_NETNS_ID] = { .type = NLA_U32 },
|
||||
};
|
||||
|
||||
static const struct genl_ops devlink_nl_ops[] = {
|
||||
|
@ -8061,9 +8165,43 @@ int devlink_compat_switch_id_get(struct net_device *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void __net_exit devlink_pernet_pre_exit(struct net *net)
|
||||
{
|
||||
struct devlink *devlink;
|
||||
int err;
|
||||
|
||||
/* In case network namespace is getting destroyed, reload
|
||||
* all devlink instances from this namespace into init_net.
|
||||
*/
|
||||
mutex_lock(&devlink_mutex);
|
||||
list_for_each_entry(devlink, &devlink_list, list) {
|
||||
if (net_eq(devlink_net(devlink), net)) {
|
||||
if (WARN_ON(!devlink_reload_supported(devlink)))
|
||||
continue;
|
||||
err = devlink_reload(devlink, &init_net, NULL);
|
||||
if (err)
|
||||
pr_warn("Failed to reload devlink instance into init_net\n");
|
||||
}
|
||||
}
|
||||
mutex_unlock(&devlink_mutex);
|
||||
}
|
||||
|
||||
static struct pernet_operations devlink_pernet_ops __net_initdata = {
|
||||
.pre_exit = devlink_pernet_pre_exit,
|
||||
};
|
||||
|
||||
static int __init devlink_init(void)
|
||||
{
|
||||
return genl_register_family(&devlink_nl_family);
|
||||
int err;
|
||||
|
||||
err = genl_register_family(&devlink_nl_family);
|
||||
if (err)
|
||||
goto out;
|
||||
err = register_pernet_subsys(&devlink_pernet_ops);
|
||||
|
||||
out:
|
||||
WARN_ON(err);
|
||||
return err;
|
||||
}
|
||||
|
||||
subsys_initcall(devlink_init);
|
||||
|
|
Loading…
Reference in New Issue
Block a user