9531 lines · 249.6 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/etherdevice.h>#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> #include "devl_internal.h" struct devlink_linecard {	struct list_head list;	struct devlink *devlink;	unsigned int index;	const struct devlink_linecard_ops *ops;	void *priv;	enum devlink_linecard_state state;	struct mutex state_lock; /* Protects state */	const char *type;	struct devlink_linecard_type *types;	unsigned int types_count;	struct devlink *nested_devlink;}; /** * struct devlink_resource - devlink resource * @name: name of the resource * @id: id, per devlink instance * @size: size of the resource * @size_new: updated size of the resource, reload is needed * @size_valid: valid in case the total size of the resource is valid *              including its children * @parent: parent resource * @size_params: size parameters * @list: parent list * @resource_list: list of child resources * @occ_get: occupancy getter callback * @occ_get_priv: occupancy getter callback priv */struct devlink_resource {	const char *name;	u64 id;	u64 size;	u64 size_new;	bool size_valid;	struct devlink_resource *parent;	struct devlink_resource_size_params size_params;	struct list_head list;	struct list_head resource_list;	devlink_resource_occ_get_t *occ_get;	void *occ_get_priv;}; 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_GPL(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_GPL(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_GPL(devlink_dpipe_header_ipv6); EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_hwmsg);EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_hwerr);EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_trap_report); #define DEVLINK_PORT_FN_CAPS_VALID_MASK \	(_BITUL(__DEVLINK_PORT_FN_ATTR_CAPS_MAX) - 1) 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),	[DEVLINK_PORT_FN_ATTR_CAPS] =		NLA_POLICY_BITFIELD32(DEVLINK_PORT_FN_CAPS_VALID_MASK),}; #define ASSERT_DEVLINK_PORT_REGISTERED(devlink_port)				\	WARN_ON_ONCE(!(devlink_port)->registered)#define ASSERT_DEVLINK_PORT_NOT_REGISTERED(devlink_port)			\	WARN_ON_ONCE((devlink_port)->registered)#define ASSERT_DEVLINK_PORT_INITIALIZED(devlink_port)				\	WARN_ON_ONCE(!(devlink_port)->initialized) static struct devlink_port *devlink_port_get_by_index(struct devlink *devlink,						      unsigned int port_index){	return xa_load(&devlink->ports, port_index);} 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);} struct devlink_port *devlink_port_get_from_info(struct devlink *devlink,						struct genl_info *info){	return devlink_port_get_from_attrs(devlink, info->attrs);} static inline booldevlink_rate_is_leaf(struct devlink_rate *devlink_rate){	return devlink_rate->type == DEVLINK_RATE_TYPE_LEAF;} static inline booldevlink_rate_is_node(struct devlink_rate *devlink_rate){	return devlink_rate->type == DEVLINK_RATE_TYPE_NODE;} static struct devlink_rate *devlink_rate_leaf_get_from_info(struct devlink *devlink, struct genl_info *info){	struct devlink_rate *devlink_rate;	struct devlink_port *devlink_port; 	devlink_port = devlink_port_get_from_attrs(devlink, info->attrs);	if (IS_ERR(devlink_port))		return ERR_CAST(devlink_port);	devlink_rate = devlink_port->devlink_rate;	return devlink_rate ?: ERR_PTR(-ENODEV);} static struct devlink_rate *devlink_rate_node_get_by_name(struct devlink *devlink, const char *node_name){	static struct devlink_rate *devlink_rate; 	list_for_each_entry(devlink_rate, &devlink->rate_list, list) {		if (devlink_rate_is_node(devlink_rate) &&		    !strcmp(node_name, devlink_rate->name))			return devlink_rate;	}	return ERR_PTR(-ENODEV);} static struct devlink_rate *devlink_rate_node_get_from_attrs(struct devlink *devlink, struct nlattr **attrs){	const char *rate_node_name;	size_t len; 	if (!attrs[DEVLINK_ATTR_RATE_NODE_NAME])		return ERR_PTR(-EINVAL);	rate_node_name = nla_data(attrs[DEVLINK_ATTR_RATE_NODE_NAME]);	len = strlen(rate_node_name);	/* Name cannot be empty or decimal number */	if (!len || strspn(rate_node_name, "0123456789") == len)		return ERR_PTR(-EINVAL); 	return devlink_rate_node_get_by_name(devlink, rate_node_name);} struct devlink_rate *devlink_rate_node_get_from_info(struct devlink *devlink, struct genl_info *info){	return devlink_rate_node_get_from_attrs(devlink, info->attrs);} struct devlink_rate *devlink_rate_get_from_info(struct devlink *devlink, struct genl_info *info){	struct nlattr **attrs = info->attrs; 	if (attrs[DEVLINK_ATTR_PORT_INDEX])		return devlink_rate_leaf_get_from_info(devlink, info);	else if (attrs[DEVLINK_ATTR_RATE_NODE_NAME])		return devlink_rate_node_get_from_info(devlink, info);	else		return ERR_PTR(-EINVAL);} static struct devlink_linecard *devlink_linecard_get_by_index(struct devlink *devlink,			      unsigned int linecard_index){	struct devlink_linecard *devlink_linecard; 	list_for_each_entry(devlink_linecard, &devlink->linecard_list, list) {		if (devlink_linecard->index == linecard_index)			return devlink_linecard;	}	return NULL;} static bool devlink_linecard_index_exists(struct devlink *devlink,					  unsigned int linecard_index){	return devlink_linecard_get_by_index(devlink, linecard_index);} static struct devlink_linecard *devlink_linecard_get_from_attrs(struct devlink *devlink, struct nlattr **attrs){	if (attrs[DEVLINK_ATTR_LINECARD_INDEX]) {		u32 linecard_index = nla_get_u32(attrs[DEVLINK_ATTR_LINECARD_INDEX]);		struct devlink_linecard *linecard; 		linecard = devlink_linecard_get_by_index(devlink, linecard_index);		if (!linecard)			return ERR_PTR(-ENODEV);		return linecard;	}	return ERR_PTR(-EINVAL);} struct devlink_linecard *devlink_linecard_get_from_info(struct devlink *devlink, struct genl_info *info){	return devlink_linecard_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 void devlink_port_fn_cap_fill(struct nla_bitfield32 *caps,				     u32 cap, bool is_enable){	caps->selector |= cap;	if (is_enable)		caps->value |= cap;} static int devlink_port_fn_roce_fill(const struct devlink_ops *ops,				     struct devlink_port *devlink_port,				     struct nla_bitfield32 *caps,				     struct netlink_ext_ack *extack){	bool is_enable;	int err; 	if (!ops->port_fn_roce_get)		return 0; 	err = ops->port_fn_roce_get(devlink_port, &is_enable, extack);	if (err) {		if (err == -EOPNOTSUPP)			return 0;		return err;	} 	devlink_port_fn_cap_fill(caps, DEVLINK_PORT_FN_CAP_ROCE, is_enable);	return 0;} static int devlink_port_fn_migratable_fill(const struct devlink_ops *ops,					   struct devlink_port *devlink_port,					   struct nla_bitfield32 *caps,					   struct netlink_ext_ack *extack){	bool is_enable;	int err; 	if (!ops->port_fn_migratable_get ||	    devlink_port->attrs.flavour != DEVLINK_PORT_FLAVOUR_PCI_VF)		return 0; 	err = ops->port_fn_migratable_get(devlink_port, &is_enable, extack);	if (err) {		if (err == -EOPNOTSUPP)			return 0;		return err;	} 	devlink_port_fn_cap_fill(caps, DEVLINK_PORT_FN_CAP_MIGRATABLE, is_enable);	return 0;} static int devlink_port_fn_caps_fill(const struct devlink_ops *ops,				     struct devlink_port *devlink_port,				     struct sk_buff *msg,				     struct netlink_ext_ack *extack,				     bool *msg_updated){

Lines 1–500 of 9531.