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; | ||||
| 
 | ||||
| 	devlink = devlink_get_from_info(info); | ||||
| 	if (~ops->internal_flags & DEVLINK_NL_FLAG_NO_LOCK) | ||||
| 	/* 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); | ||||
| 		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