1443 lines · 37.8 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * Copyright (c) 2016 Mellanox Technologies. All rights reserved. * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com> */ #include <linux/device.h>#include <net/genetlink.h>#include <net/sock.h>#include "devl_internal.h" struct devlink_info_req {	struct sk_buff *msg;	void (*version_cb)(const char *version_name,			   enum devlink_info_version_type version_type,			   void *version_cb_priv);	void *version_cb_priv;}; struct devlink_reload_combination {	enum devlink_reload_action action;	enum devlink_reload_limit limit;}; static const struct devlink_reload_combination devlink_reload_invalid_combinations[] = {	{		/* can't reinitialize driver with no down time */		.action = DEVLINK_RELOAD_ACTION_DRIVER_REINIT,		.limit = DEVLINK_RELOAD_LIMIT_NO_RESET,	},}; static booldevlink_reload_combination_is_invalid(enum devlink_reload_action action,				      enum devlink_reload_limit limit){	int i; 	for (i = 0; i < ARRAY_SIZE(devlink_reload_invalid_combinations); i++)		if (devlink_reload_invalid_combinations[i].action == action &&		    devlink_reload_invalid_combinations[i].limit == limit)			return true;	return false;} static booldevlink_reload_action_is_supported(struct devlink *devlink, enum devlink_reload_action action){	return test_bit(action, &devlink->ops->reload_actions);} static booldevlink_reload_limit_is_supported(struct devlink *devlink, enum devlink_reload_limit limit){	return test_bit(limit, &devlink->ops->reload_limits);} static int devlink_reload_stat_put(struct sk_buff *msg,				   enum devlink_reload_limit limit, u32 value){	struct nlattr *reload_stats_entry; 	reload_stats_entry = nla_nest_start(msg, DEVLINK_ATTR_RELOAD_STATS_ENTRY);	if (!reload_stats_entry)		return -EMSGSIZE; 	if (nla_put_u8(msg, DEVLINK_ATTR_RELOAD_STATS_LIMIT, limit) ||	    nla_put_u32(msg, DEVLINK_ATTR_RELOAD_STATS_VALUE, value))		goto nla_put_failure;	nla_nest_end(msg, reload_stats_entry);	return 0; nla_put_failure:	nla_nest_cancel(msg, reload_stats_entry);	return -EMSGSIZE;} static intdevlink_reload_stats_put(struct sk_buff *msg, struct devlink *devlink, bool is_remote){	struct nlattr *reload_stats_attr, *act_info, *act_stats;	int i, j, stat_idx;	u32 value; 	if (!is_remote)		reload_stats_attr = nla_nest_start(msg, DEVLINK_ATTR_RELOAD_STATS);	else		reload_stats_attr = nla_nest_start(msg, DEVLINK_ATTR_REMOTE_RELOAD_STATS); 	if (!reload_stats_attr)		return -EMSGSIZE; 	for (i = 0; i <= DEVLINK_RELOAD_ACTION_MAX; i++) {		if ((!is_remote &&		     !devlink_reload_action_is_supported(devlink, i)) ||		    i == DEVLINK_RELOAD_ACTION_UNSPEC)			continue;		act_info = nla_nest_start(msg, DEVLINK_ATTR_RELOAD_ACTION_INFO);		if (!act_info)			goto nla_put_failure; 		if (nla_put_u8(msg, DEVLINK_ATTR_RELOAD_ACTION, i))			goto action_info_nest_cancel;		act_stats = nla_nest_start(msg, DEVLINK_ATTR_RELOAD_ACTION_STATS);		if (!act_stats)			goto action_info_nest_cancel; 		for (j = 0; j <= DEVLINK_RELOAD_LIMIT_MAX; j++) {			/* Remote stats are shown even if not locally supported.			 * Stats of actions with unspecified limit are shown			 * though drivers don't need to register unspecified			 * limit.			 */			if ((!is_remote && j != DEVLINK_RELOAD_LIMIT_UNSPEC &&			     !devlink_reload_limit_is_supported(devlink, j)) ||			    devlink_reload_combination_is_invalid(i, j))				continue; 			stat_idx = j * __DEVLINK_RELOAD_ACTION_MAX + i;			if (!is_remote)				value = devlink->stats.reload_stats[stat_idx];			else				value = devlink->stats.remote_reload_stats[stat_idx];			if (devlink_reload_stat_put(msg, j, value))				goto action_stats_nest_cancel;		}		nla_nest_end(msg, act_stats);		nla_nest_end(msg, act_info);	}	nla_nest_end(msg, reload_stats_attr);	return 0; action_stats_nest_cancel:	nla_nest_cancel(msg, act_stats);action_info_nest_cancel:	nla_nest_cancel(msg, act_info);nla_put_failure:	nla_nest_cancel(msg, reload_stats_attr);	return -EMSGSIZE;} static int devlink_nl_nested_fill(struct sk_buff *msg, struct devlink *devlink){	unsigned long rel_index;	void *unused;	int err; 	xa_for_each(&devlink->nested_rels, rel_index, unused) {		err = devlink_rel_devlink_handle_put(msg, devlink,						     rel_index,						     DEVLINK_ATTR_NESTED_DEVLINK,						     NULL);		if (err)			return err;	}	return 0;} static int devlink_nl_fill(struct sk_buff *msg, struct devlink *devlink,			   enum devlink_command cmd, u32 portid,			   u32 seq, int flags){	struct nlattr *dev_stats;	void *hdr; 	hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);	if (!hdr)		return -EMSGSIZE; 	if (devlink_nl_put_handle(msg, devlink))		goto nla_put_failure;	if (nla_put_u8(msg, DEVLINK_ATTR_RELOAD_FAILED, devlink->reload_failed))		goto nla_put_failure; 	dev_stats = nla_nest_start(msg, DEVLINK_ATTR_DEV_STATS);	if (!dev_stats)		goto nla_put_failure; 	if (devlink_reload_stats_put(msg, devlink, false))		goto dev_stats_nest_cancel;	if (devlink_reload_stats_put(msg, devlink, true))		goto dev_stats_nest_cancel; 	nla_nest_end(msg, dev_stats); 	if (devlink_nl_nested_fill(msg, devlink))		goto nla_put_failure; 	genlmsg_end(msg, hdr);	return 0; dev_stats_nest_cancel:	nla_nest_cancel(msg, dev_stats);nla_put_failure:	genlmsg_cancel(msg, hdr);	return -EMSGSIZE;} static void devlink_notify(struct devlink *devlink, enum devlink_command cmd){	struct sk_buff *msg;	int err; 	WARN_ON(cmd != DEVLINK_CMD_NEW && cmd != DEVLINK_CMD_DEL);	WARN_ON(!devl_is_registered(devlink)); 	if (!devlink_nl_notify_need(devlink))		return; 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);	if (!msg)		return; 	err = devlink_nl_fill(msg, devlink, cmd, 0, 0, 0);	if (err) {		nlmsg_free(msg);		return;	} 	devlink_nl_notify_send(devlink, msg);} int devlink_nl_get_doit(struct sk_buff *skb, struct genl_info *info){	struct devlink *devlink = info->user_ptr[0];	struct sk_buff *msg;	int err; 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);	if (!msg)		return -ENOMEM; 	err = devlink_nl_fill(msg, devlink, DEVLINK_CMD_NEW,			      info->snd_portid, info->snd_seq, 0);	if (err) {		nlmsg_free(msg);		return err;	} 	return genlmsg_reply(msg, info);} static intdevlink_nl_get_dump_one(struct sk_buff *msg, struct devlink *devlink,			struct netlink_callback *cb, int flags){	return devlink_nl_fill(msg, devlink, DEVLINK_CMD_NEW,			       NETLINK_CB(cb->skb).portid,			       cb->nlh->nlmsg_seq, flags);} int devlink_nl_get_dumpit(struct sk_buff *msg, struct netlink_callback *cb){	return devlink_nl_dumpit(msg, cb, devlink_nl_get_dump_one);} static void devlink_rel_notify_cb(struct devlink *devlink, u32 obj_index){	devlink_notify(devlink, DEVLINK_CMD_NEW);} static void devlink_rel_cleanup_cb(struct devlink *devlink, u32 obj_index,				   u32 rel_index){	xa_erase(&devlink->nested_rels, rel_index);} int devl_nested_devlink_set(struct devlink *devlink,			    struct devlink *nested_devlink){	u32 rel_index;	int err; 	err = devlink_rel_nested_in_add(&rel_index, devlink->index, 0,					devlink_rel_notify_cb,					devlink_rel_cleanup_cb,					nested_devlink);	if (err)		return err;	return xa_insert(&devlink->nested_rels, rel_index,			 xa_mk_value(0), GFP_KERNEL);}EXPORT_SYMBOL_GPL(devl_nested_devlink_set); void devlink_notify_register(struct devlink *devlink){	devlink_notify(devlink, DEVLINK_CMD_NEW);	devlink_linecards_notify_register(devlink);	devlink_ports_notify_register(devlink);	devlink_trap_policers_notify_register(devlink);	devlink_trap_groups_notify_register(devlink);	devlink_traps_notify_register(devlink);	devlink_rates_notify_register(devlink);	devlink_regions_notify_register(devlink);	devlink_params_notify_register(devlink);} void devlink_notify_unregister(struct devlink *devlink){	devlink_params_notify_unregister(devlink);	devlink_regions_notify_unregister(devlink);	devlink_rates_notify_unregister(devlink);	devlink_traps_notify_unregister(devlink);	devlink_trap_groups_notify_unregister(devlink);	devlink_trap_policers_notify_unregister(devlink);	devlink_ports_notify_unregister(devlink);	devlink_linecards_notify_unregister(devlink);	devlink_notify(devlink, DEVLINK_CMD_DEL);} static void devlink_reload_failed_set(struct devlink *devlink,				      bool reload_failed){	if (devlink->reload_failed == reload_failed)		return;	devlink->reload_failed = reload_failed;	devlink_notify(devlink, DEVLINK_CMD_NEW);} bool devlink_is_reload_failed(const struct devlink *devlink){	return devlink->reload_failed;}EXPORT_SYMBOL_GPL(devlink_is_reload_failed); static void__devlink_reload_stats_update(struct devlink *devlink, u32 *reload_stats,			      enum devlink_reload_limit limit, u32 actions_performed){	unsigned long actions = actions_performed;	int stat_idx;	int action; 	for_each_set_bit(action, &actions, __DEVLINK_RELOAD_ACTION_MAX) {		stat_idx = limit * __DEVLINK_RELOAD_ACTION_MAX + action;		reload_stats[stat_idx]++;	}	devlink_notify(devlink, DEVLINK_CMD_NEW);} static voiddevlink_reload_stats_update(struct devlink *devlink, enum devlink_reload_limit limit,			    u32 actions_performed){	__devlink_reload_stats_update(devlink, devlink->stats.reload_stats, limit,				      actions_performed);} /** *	devlink_remote_reload_actions_performed - Update devlink on reload actions *	  performed which are not a direct result of devlink reload call. * *	This should be called by a driver after performing reload actions in case it was not *	a result of devlink reload call. For example fw_activate was performed as a result *	of devlink reload triggered fw_activate on another host. *	The motivation for this function is to keep data on reload actions performed on this *	function whether it was done due to direct devlink reload call or not. * *	@devlink: devlink *	@limit: reload limit *	@actions_performed: bitmask of actions performed */void devlink_remote_reload_actions_performed(struct devlink *devlink,					     enum devlink_reload_limit limit,					     u32 actions_performed){	if (WARN_ON(!actions_performed ||		    actions_performed & BIT(DEVLINK_RELOAD_ACTION_UNSPEC) ||		    actions_performed >= BIT(__DEVLINK_RELOAD_ACTION_MAX) ||		    limit > DEVLINK_RELOAD_LIMIT_MAX))		return; 	__devlink_reload_stats_update(devlink, devlink->stats.remote_reload_stats, limit,				      actions_performed);}EXPORT_SYMBOL_GPL(devlink_remote_reload_actions_performed); 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_reload_netns_change(struct devlink *devlink,					struct net *curr_net,					struct net *dest_net){	/* 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.	 */	devlink_notify_unregister(devlink);	write_pnet(&devlink->_net, dest_net);	devlink_notify_register(devlink);	devlink_rel_nested_in_notify(devlink);} static void devlink_reload_reinit_sanity_check(struct devlink *devlink){	WARN_ON(!list_empty(&devlink->trap_policer_list));	WARN_ON(!list_empty(&devlink->trap_group_list));	WARN_ON(!list_empty(&devlink->trap_list));	WARN_ON(!list_empty(&devlink->dpipe_table_list));	WARN_ON(!list_empty(&devlink->sb_list));	WARN_ON(devlink_rates_check(devlink, NULL, NULL));	WARN_ON(!list_empty(&devlink->linecard_list));	WARN_ON(!xa_empty(&devlink->ports));} int devlink_reload(struct devlink *devlink, struct net *dest_net,		   enum devlink_reload_action action,		   enum devlink_reload_limit limit,		   u32 *actions_performed, struct netlink_ext_ack *extack){	u32 remote_reload_stats[DEVLINK_RELOAD_STATS_ARRAY_SIZE];	struct net *curr_net;	int err; 	/* Make sure the reload operations are invoked with the device lock	 * held to allow drivers to trigger functionality that expects it	 * (e.g., PCI reset) and to close possible races between these	 * operations and probe/remove.	 */	device_lock_assert(devlink->dev); 	memcpy(remote_reload_stats, devlink->stats.remote_reload_stats,	       sizeof(remote_reload_stats)); 	err = devlink->ops->reload_down(devlink, !!dest_net, action, limit, extack);	if (err)		return err; 	curr_net = devlink_net(devlink);	if (dest_net && !net_eq(dest_net, curr_net))		devlink_reload_netns_change(devlink, curr_net, dest_net); 	if (action == DEVLINK_RELOAD_ACTION_DRIVER_REINIT) {		devlink_params_driverinit_load_new(devlink);		devlink_reload_reinit_sanity_check(devlink);	} 	err = devlink->ops->reload_up(devlink, action, limit, actions_performed, extack);	devlink_reload_failed_set(devlink, !!err);	if (err)		return err; 	WARN_ON(!(*actions_performed & BIT(action)));	/* Catch driver on updating the remote action within devlink reload */	WARN_ON(memcmp(remote_reload_stats, devlink->stats.remote_reload_stats,		       sizeof(remote_reload_stats)));	devlink_reload_stats_update(devlink, limit, *actions_performed);	return 0;} static intdevlink_nl_reload_actions_performed_snd(struct devlink *devlink, u32 actions_performed,					enum devlink_command cmd, struct genl_info *info){	struct sk_buff *msg;	void *hdr; 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);	if (!msg)		return -ENOMEM; 	hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, &devlink_nl_family, 0, cmd);	if (!hdr)		goto free_msg;

Lines 1–500 of 1443.