10613 lines · 273.3 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * net/core/devlink.c - Network physical/parent device Netlink interface * * Heavily inspired by net/wireless/ * Copyright (c) 2016 Mellanox Technologies. All rights reserved. * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com> */ #include <linux/kernel.h>#include <linux/module.h>#include <linux/types.h>#include <linux/slab.h>#include <linux/gfp.h>#include <linux/device.h>#include <linux/list.h>#include <linux/netdevice.h>#include <linux/spinlock.h>#include <linux/refcount.h>#include <linux/workqueue.h>#include <linux/u64_stats_sync.h>#include <linux/timekeeping.h>#include <rdma/ib_verbs.h>#include <net/netlink.h>#include <net/genetlink.h>#include <net/rtnetlink.h>#include <net/net_namespace.h>#include <net/sock.h>#include <net/devlink.h>#define CREATE_TRACE_POINTS#include <trace/events/devlink.h> static struct devlink_dpipe_field devlink_dpipe_fields_ethernet[] = {	{		.name = "destination mac",		.id = DEVLINK_DPIPE_FIELD_ETHERNET_DST_MAC,		.bitwidth = 48,	},}; struct devlink_dpipe_header devlink_dpipe_header_ethernet = {	.name = "ethernet",	.id = DEVLINK_DPIPE_HEADER_ETHERNET,	.fields = devlink_dpipe_fields_ethernet,	.fields_count = ARRAY_SIZE(devlink_dpipe_fields_ethernet),	.global = true,};EXPORT_SYMBOL(devlink_dpipe_header_ethernet); static struct devlink_dpipe_field devlink_dpipe_fields_ipv4[] = {	{		.name = "destination ip",		.id = DEVLINK_DPIPE_FIELD_IPV4_DST_IP,		.bitwidth = 32,	},}; struct devlink_dpipe_header devlink_dpipe_header_ipv4 = {	.name = "ipv4",	.id = DEVLINK_DPIPE_HEADER_IPV4,	.fields = devlink_dpipe_fields_ipv4,	.fields_count = ARRAY_SIZE(devlink_dpipe_fields_ipv4),	.global = true,};EXPORT_SYMBOL(devlink_dpipe_header_ipv4); static struct devlink_dpipe_field devlink_dpipe_fields_ipv6[] = {	{		.name = "destination ip",		.id = DEVLINK_DPIPE_FIELD_IPV6_DST_IP,		.bitwidth = 128,	},}; struct devlink_dpipe_header devlink_dpipe_header_ipv6 = {	.name = "ipv6",	.id = DEVLINK_DPIPE_HEADER_IPV6,	.fields = devlink_dpipe_fields_ipv6,	.fields_count = ARRAY_SIZE(devlink_dpipe_fields_ipv6),	.global = true,};EXPORT_SYMBOL(devlink_dpipe_header_ipv6); EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_hwmsg);EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_hwerr);EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_trap_report); static const struct nla_policy devlink_function_nl_policy[DEVLINK_PORT_FUNCTION_ATTR_MAX + 1] = {	[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR] = { .type = NLA_BINARY },	[DEVLINK_PORT_FN_ATTR_STATE] =		NLA_POLICY_RANGE(NLA_U8, DEVLINK_PORT_FN_STATE_INACTIVE,				 DEVLINK_PORT_FN_STATE_ACTIVE),}; static LIST_HEAD(devlink_list); /* devlink_mutex * * An overall lock guarding every operation coming from userspace. * It also guards devlink devices list and it is taken when * driver registers/unregisters it. */static DEFINE_MUTEX(devlink_mutex); struct net *devlink_net(const struct devlink *devlink){	return read_pnet(&devlink->_net);}EXPORT_SYMBOL_GPL(devlink_net); static void __devlink_net_set(struct devlink *devlink, struct net *net){	write_pnet(&devlink->_net, net);} void devlink_net_set(struct devlink *devlink, struct net *net){	if (WARN_ON(devlink->registered))		return;	__devlink_net_set(devlink, net);}EXPORT_SYMBOL_GPL(devlink_net_set); static struct devlink *devlink_get_from_attrs(struct net *net,					      struct nlattr **attrs){	struct devlink *devlink;	char *busname;	char *devname; 	if (!attrs[DEVLINK_ATTR_BUS_NAME] || !attrs[DEVLINK_ATTR_DEV_NAME])		return ERR_PTR(-EINVAL); 	busname = nla_data(attrs[DEVLINK_ATTR_BUS_NAME]);	devname = nla_data(attrs[DEVLINK_ATTR_DEV_NAME]); 	lockdep_assert_held(&devlink_mutex); 	list_for_each_entry(devlink, &devlink_list, list) {		if (strcmp(devlink->dev->bus->name, busname) == 0 &&		    strcmp(dev_name(devlink->dev), devname) == 0 &&		    net_eq(devlink_net(devlink), net))			return devlink;	} 	return ERR_PTR(-ENODEV);} static struct devlink *devlink_get_from_info(struct genl_info *info){	return devlink_get_from_attrs(genl_info_net(info), info->attrs);} static struct devlink_port *devlink_port_get_by_index(struct devlink *devlink,						      unsigned int port_index){	struct devlink_port *devlink_port; 	list_for_each_entry(devlink_port, &devlink->port_list, list) {		if (devlink_port->index == port_index)			return devlink_port;	}	return NULL;} static bool devlink_port_index_exists(struct devlink *devlink,				      unsigned int port_index){	return devlink_port_get_by_index(devlink, port_index);} static struct devlink_port *devlink_port_get_from_attrs(struct devlink *devlink,							struct nlattr **attrs){	if (attrs[DEVLINK_ATTR_PORT_INDEX]) {		u32 port_index = nla_get_u32(attrs[DEVLINK_ATTR_PORT_INDEX]);		struct devlink_port *devlink_port; 		devlink_port = devlink_port_get_by_index(devlink, port_index);		if (!devlink_port)			return ERR_PTR(-ENODEV);		return devlink_port;	}	return ERR_PTR(-EINVAL);} static struct devlink_port *devlink_port_get_from_info(struct devlink *devlink,						       struct genl_info *info){	return devlink_port_get_from_attrs(devlink, info->attrs);} struct devlink_sb {	struct list_head list;	unsigned int index;	u32 size;	u16 ingress_pools_count;	u16 egress_pools_count;	u16 ingress_tc_count;	u16 egress_tc_count;}; static u16 devlink_sb_pool_count(struct devlink_sb *devlink_sb){	return devlink_sb->ingress_pools_count + devlink_sb->egress_pools_count;} static struct devlink_sb *devlink_sb_get_by_index(struct devlink *devlink,						  unsigned int sb_index){	struct devlink_sb *devlink_sb; 	list_for_each_entry(devlink_sb, &devlink->sb_list, list) {		if (devlink_sb->index == sb_index)			return devlink_sb;	}	return NULL;} static bool devlink_sb_index_exists(struct devlink *devlink,				    unsigned int sb_index){	return devlink_sb_get_by_index(devlink, sb_index);} static struct devlink_sb *devlink_sb_get_from_attrs(struct devlink *devlink,						    struct nlattr **attrs){	if (attrs[DEVLINK_ATTR_SB_INDEX]) {		u32 sb_index = nla_get_u32(attrs[DEVLINK_ATTR_SB_INDEX]);		struct devlink_sb *devlink_sb; 		devlink_sb = devlink_sb_get_by_index(devlink, sb_index);		if (!devlink_sb)			return ERR_PTR(-ENODEV);		return devlink_sb;	}	return ERR_PTR(-EINVAL);} static struct devlink_sb *devlink_sb_get_from_info(struct devlink *devlink,						   struct genl_info *info){	return devlink_sb_get_from_attrs(devlink, info->attrs);} static int devlink_sb_pool_index_get_from_attrs(struct devlink_sb *devlink_sb,						struct nlattr **attrs,						u16 *p_pool_index){	u16 val; 	if (!attrs[DEVLINK_ATTR_SB_POOL_INDEX])		return -EINVAL; 	val = nla_get_u16(attrs[DEVLINK_ATTR_SB_POOL_INDEX]);	if (val >= devlink_sb_pool_count(devlink_sb))		return -EINVAL;	*p_pool_index = val;	return 0;} static int devlink_sb_pool_index_get_from_info(struct devlink_sb *devlink_sb,					       struct genl_info *info,					       u16 *p_pool_index){	return devlink_sb_pool_index_get_from_attrs(devlink_sb, info->attrs,						    p_pool_index);} static intdevlink_sb_pool_type_get_from_attrs(struct nlattr **attrs,				    enum devlink_sb_pool_type *p_pool_type){	u8 val; 	if (!attrs[DEVLINK_ATTR_SB_POOL_TYPE])		return -EINVAL; 	val = nla_get_u8(attrs[DEVLINK_ATTR_SB_POOL_TYPE]);	if (val != DEVLINK_SB_POOL_TYPE_INGRESS &&	    val != DEVLINK_SB_POOL_TYPE_EGRESS)		return -EINVAL;	*p_pool_type = val;	return 0;} static intdevlink_sb_pool_type_get_from_info(struct genl_info *info,				   enum devlink_sb_pool_type *p_pool_type){	return devlink_sb_pool_type_get_from_attrs(info->attrs, p_pool_type);} static intdevlink_sb_th_type_get_from_attrs(struct nlattr **attrs,				  enum devlink_sb_threshold_type *p_th_type){	u8 val; 	if (!attrs[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE])		return -EINVAL; 	val = nla_get_u8(attrs[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE]);	if (val != DEVLINK_SB_THRESHOLD_TYPE_STATIC &&	    val != DEVLINK_SB_THRESHOLD_TYPE_DYNAMIC)		return -EINVAL;	*p_th_type = val;	return 0;} static intdevlink_sb_th_type_get_from_info(struct genl_info *info,				 enum devlink_sb_threshold_type *p_th_type){	return devlink_sb_th_type_get_from_attrs(info->attrs, p_th_type);} static intdevlink_sb_tc_index_get_from_attrs(struct devlink_sb *devlink_sb,				   struct nlattr **attrs,				   enum devlink_sb_pool_type pool_type,				   u16 *p_tc_index){	u16 val; 	if (!attrs[DEVLINK_ATTR_SB_TC_INDEX])		return -EINVAL; 	val = nla_get_u16(attrs[DEVLINK_ATTR_SB_TC_INDEX]);	if (pool_type == DEVLINK_SB_POOL_TYPE_INGRESS &&	    val >= devlink_sb->ingress_tc_count)		return -EINVAL;	if (pool_type == DEVLINK_SB_POOL_TYPE_EGRESS &&	    val >= devlink_sb->egress_tc_count)		return -EINVAL;	*p_tc_index = val;	return 0;} static intdevlink_sb_tc_index_get_from_info(struct devlink_sb *devlink_sb,				  struct genl_info *info,				  enum devlink_sb_pool_type pool_type,				  u16 *p_tc_index){	return devlink_sb_tc_index_get_from_attrs(devlink_sb, info->attrs,						  pool_type, p_tc_index);} struct devlink_region {	struct devlink *devlink;	struct devlink_port *port;	struct list_head list;	union {		const struct devlink_region_ops *ops;		const struct devlink_port_region_ops *port_ops;	};	struct list_head snapshot_list;	u32 max_snapshots;	u32 cur_snapshots;	u64 size;}; struct devlink_snapshot {	struct list_head list;	struct devlink_region *region;	u8 *data;	u32 id;}; static struct devlink_region *devlink_region_get_by_name(struct devlink *devlink, const char *region_name){	struct devlink_region *region; 	list_for_each_entry(region, &devlink->region_list, list)		if (!strcmp(region->ops->name, region_name))			return region; 	return NULL;} static struct devlink_region *devlink_port_region_get_by_name(struct devlink_port *port,				const char *region_name){	struct devlink_region *region; 	list_for_each_entry(region, &port->region_list, list)		if (!strcmp(region->ops->name, region_name))			return region; 	return NULL;} static struct devlink_snapshot *devlink_region_snapshot_get_by_id(struct devlink_region *region, u32 id){	struct devlink_snapshot *snapshot; 	list_for_each_entry(snapshot, &region->snapshot_list, list)		if (snapshot->id == id)			return snapshot; 	return NULL;} #define DEVLINK_NL_FLAG_NEED_PORT		BIT(0)#define DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT	BIT(1) /* The per devlink instance lock is taken by default in the pre-doit * operation, yet several commands do not require this. The global * devlink lock is taken and protects from disruption by user-calls. */#define DEVLINK_NL_FLAG_NO_LOCK			BIT(2) static int devlink_nl_pre_doit(const struct genl_ops *ops,			       struct sk_buff *skb, struct genl_info *info){	struct devlink_port *devlink_port;	struct devlink *devlink;	int err; 	mutex_lock(&devlink_mutex);	devlink = devlink_get_from_info(info);	if (IS_ERR(devlink)) {		mutex_unlock(&devlink_mutex);		return PTR_ERR(devlink);	}	if (~ops->internal_flags & DEVLINK_NL_FLAG_NO_LOCK)		mutex_lock(&devlink->lock);	info->user_ptr[0] = devlink;	if (ops->internal_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;	} else if (ops->internal_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;	}	return 0; unlock:	if (~ops->internal_flags & DEVLINK_NL_FLAG_NO_LOCK)		mutex_unlock(&devlink->lock);	mutex_unlock(&devlink_mutex);	return err;} static void devlink_nl_post_doit(const struct genl_ops *ops,				 struct sk_buff *skb, struct genl_info *info){	struct devlink *devlink; 	devlink = info->user_ptr[0];	if (~ops->internal_flags & DEVLINK_NL_FLAG_NO_LOCK)		mutex_unlock(&devlink->lock);	mutex_unlock(&devlink_mutex);} static struct genl_family devlink_nl_family; enum devlink_multicast_groups {	DEVLINK_MCGRP_CONFIG,}; static const struct genl_multicast_group devlink_nl_mcgrps[] = {	[DEVLINK_MCGRP_CONFIG] = { .name = DEVLINK_GENL_MCGRP_CONFIG_NAME },}; static int devlink_nl_put_handle(struct sk_buff *msg, struct devlink *devlink){	if (nla_put_string(msg, DEVLINK_ATTR_BUS_NAME, devlink->dev->bus->name))		return -EMSGSIZE;	if (nla_put_string(msg, DEVLINK_ATTR_DEV_NAME, dev_name(devlink->dev)))		return -EMSGSIZE;	return 0;} 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)

Lines 1–500 of 10613.