1031 lines · 28.4 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 "devl_internal.h" static inline booldevlink_rate_is_leaf(struct devlink_rate *devlink_rate){	return devlink_rate->type == DEVLINK_RATE_TYPE_LEAF;} bool devlink_rate_is_node(const 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);} /* Repeatedly walks the nested devlink chain while cross device rate nodes are * supported and finds the topmost instance where rates should be stored. * That instance is locked, referenced and returned. * When cross device rate nodes aren't supported the original devlink instance * is returned. */static struct devlink *devl_rate_lock(struct devlink *devlink){	struct devlink *rate_devlink = devlink, *parent; 	devl_assert_locked(devlink); 	while (rate_devlink->ops &&	       rate_devlink->ops->supported_cross_device_rate_nodes) {		parent = devlink_nested_in_get_lock(rate_devlink);		if (!parent)			break;		if (rate_devlink != devlink) {			/* Unlock intermediate instances. */			devl_unlock(rate_devlink);			devlink_put(rate_devlink);		}		rate_devlink = parent;	}	return rate_devlink;} /* Unlocks and puts 'rate devlink' if different than 'devlink'. */static void devl_rate_unlock(struct devlink *devlink,			     struct devlink *rate_devlink){	if (devlink == rate_devlink)		return; 	devl_unlock(rate_devlink);	devlink_put(rate_devlink);} static struct devlink_rate *devlink_rate_node_get_by_name(struct devlink *rate_devlink,			      struct devlink *devlink, const char *node_name){	struct devlink_rate *devlink_rate; 	list_for_each_entry(devlink_rate, &rate_devlink->rate_list, list) {		if (devlink_rate->devlink == devlink &&		    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 *rate_devlink,				 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(rate_devlink, devlink,					     rate_node_name);} static struct devlink_rate *devlink_rate_node_get_from_info(struct devlink *rate_devlink,				struct devlink *devlink,				struct genl_info *info){	return devlink_rate_node_get_from_attrs(rate_devlink, devlink,						info->attrs);} static struct devlink_rate *devlink_rate_get_from_info(struct devlink *rate_devlink,			   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(rate_devlink, devlink,						       info);	else		return ERR_PTR(-EINVAL);} static int devlink_rate_put_tc_bws(struct sk_buff *msg, u32 *tc_bw){	struct nlattr *nla_tc_bw;	int i; 	for (i = 0; i < DEVLINK_RATE_TCS_MAX; i++) {		nla_tc_bw = nla_nest_start(msg, DEVLINK_ATTR_RATE_TC_BWS);		if (!nla_tc_bw)			return -EMSGSIZE; 		if (nla_put_u8(msg, DEVLINK_RATE_TC_ATTR_INDEX, i) ||		    nla_put_u32(msg, DEVLINK_RATE_TC_ATTR_BW, tc_bw[i]))			goto nla_put_failure; 		nla_nest_end(msg, nla_tc_bw);	}	return 0; nla_put_failure:	nla_nest_cancel(msg, nla_tc_bw);	return -EMSGSIZE;} static int devlink_nl_rate_parent_fill(struct sk_buff *msg,				       struct devlink_rate *devlink_rate){	struct devlink_rate *parent = devlink_rate->parent;	struct devlink *devlink = parent->devlink; 	if (nla_put_string(msg, DEVLINK_ATTR_RATE_PARENT_NODE_NAME,			   parent->name))		return -EMSGSIZE; 	if (devlink != devlink_rate->devlink &&	    devlink_nl_put_nested_handle(msg,					 devlink_net(devlink_rate->devlink),					 devlink, DEVLINK_ATTR_PARENT_DEV))		return -EMSGSIZE; 	return 0;} static int devlink_nl_rate_fill(struct sk_buff *msg,				struct devlink_rate *devlink_rate,				enum devlink_command cmd, u32 portid, u32 seq,				int flags, struct netlink_ext_ack *extack){	struct devlink *devlink = devlink_rate->devlink;	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_u16(msg, DEVLINK_ATTR_RATE_TYPE, devlink_rate->type))		goto nla_put_failure; 	if (devlink_rate_is_leaf(devlink_rate)) {		if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX,				devlink_rate->devlink_port->index))			goto nla_put_failure;	} else if (devlink_rate_is_node(devlink_rate)) {		if (nla_put_string(msg, DEVLINK_ATTR_RATE_NODE_NAME,				   devlink_rate->name))			goto nla_put_failure;	} 	if (devlink_nl_put_u64(msg, DEVLINK_ATTR_RATE_TX_SHARE,			       devlink_rate->tx_share))		goto nla_put_failure; 	if (devlink_nl_put_u64(msg, DEVLINK_ATTR_RATE_TX_MAX,			       devlink_rate->tx_max))		goto nla_put_failure; 	if (nla_put_u32(msg, DEVLINK_ATTR_RATE_TX_PRIORITY,			devlink_rate->tx_priority))		goto nla_put_failure; 	if (nla_put_u32(msg, DEVLINK_ATTR_RATE_TX_WEIGHT,			devlink_rate->tx_weight))		goto nla_put_failure; 	if (devlink_rate->parent &&	    devlink_nl_rate_parent_fill(msg, devlink_rate))		goto nla_put_failure; 	if (devlink_rate_put_tc_bws(msg, devlink_rate->tc_bw))		goto nla_put_failure; 	genlmsg_end(msg, hdr);	return 0; nla_put_failure:	genlmsg_cancel(msg, hdr);	return -EMSGSIZE;} static void devlink_rate_notify(struct devlink_rate *devlink_rate,				enum devlink_command cmd){	struct devlink *devlink = devlink_rate->devlink;	struct sk_buff *msg;	int err; 	WARN_ON(cmd != DEVLINK_CMD_RATE_NEW && cmd != DEVLINK_CMD_RATE_DEL); 	if (!devl_is_registered(devlink) || !devlink_nl_notify_need(devlink))		return; 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);	if (!msg)		return; 	err = devlink_nl_rate_fill(msg, devlink_rate, cmd, 0, 0, 0, NULL);	if (err) {		nlmsg_free(msg);		return;	} 	devlink_nl_notify_send(devlink, msg);} void devlink_rates_notify_register(struct devlink *devlink){	struct devlink_rate *rate_node;	struct devlink *rate_devlink; 	rate_devlink = devl_rate_lock(devlink);	list_for_each_entry(rate_node, &rate_devlink->rate_list, list)		if (rate_node->devlink == devlink)			devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_NEW);	devl_rate_unlock(devlink, rate_devlink);} void devlink_rates_notify_unregister(struct devlink *devlink){	struct devlink_rate *rate_node;	struct devlink *rate_devlink; 	rate_devlink = devl_rate_lock(devlink);	list_for_each_entry_reverse(rate_node, &rate_devlink->rate_list, list)		if (rate_node->devlink == devlink)			devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_DEL);	devl_rate_unlock(devlink, rate_devlink);} static intdevlink_nl_rate_get_dump_one(struct sk_buff *msg, struct devlink *devlink,			     struct netlink_callback *cb, int flags){	struct devlink_nl_dump_state *state = devlink_dump_state(cb);	struct devlink_rate *devlink_rate;	struct devlink *rate_devlink;	int idx = 0;	int err = 0; 	rate_devlink = devl_rate_lock(devlink);	list_for_each_entry(devlink_rate, &rate_devlink->rate_list, list) {		enum devlink_command cmd = DEVLINK_CMD_RATE_NEW;		u32 id = NETLINK_CB(cb->skb).portid; 		if (idx < state->idx || devlink_rate->devlink != devlink) {			idx++;			continue;		} 		err = devlink_nl_rate_fill(msg, devlink_rate, cmd, id,					   cb->nlh->nlmsg_seq, flags, NULL);		if (err) {			state->idx = idx;			break;		}		idx++;	}	devl_rate_unlock(devlink, rate_devlink); 	return err;} int devlink_nl_rate_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb){	return devlink_nl_dumpit(skb, cb, devlink_nl_rate_get_dump_one);} int devlink_nl_rate_get_doit(struct sk_buff *skb, struct genl_info *info){	struct devlink *rate_devlink, *devlink = devlink_nl_ctx(info)->devlink;	struct devlink_rate *devlink_rate;	struct sk_buff *msg;	int err; 	rate_devlink = devl_rate_lock(devlink);	devlink_rate = devlink_rate_get_from_info(rate_devlink, devlink, info);	if (IS_ERR(devlink_rate)) {		err = PTR_ERR(devlink_rate);		goto unlock;	} 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);	if (!msg) {		err = -ENOMEM;		goto unlock;	} 	err = devlink_nl_rate_fill(msg, devlink_rate, DEVLINK_CMD_RATE_NEW,				   info->snd_portid, info->snd_seq, 0,				   info->extack);	if (err)		goto err_fill; 	devl_rate_unlock(devlink, rate_devlink);	return genlmsg_reply(msg, info); err_fill:	nlmsg_free(msg);unlock:	devl_rate_unlock(devlink, rate_devlink);	return err;} static booldevlink_rate_is_parent_node(struct devlink_rate *devlink_rate,			    struct devlink_rate *parent){	while (parent) {		if (parent == devlink_rate)			return true;		parent = parent->parent;	}	return false;} static intdevlink_nl_rate_parent_node_set(struct devlink_rate *devlink_rate,				struct devlink *rate_devlink,				struct genl_info *info,				struct nlattr *nla_parent){	struct devlink *devlink = devlink_rate->devlink, *parent_devlink;	const char *parent_name = nla_data(nla_parent);	const struct devlink_ops *ops = devlink->ops;	size_t len = strlen(parent_name);	struct devlink_rate *parent;	int err = -EOPNOTSUPP; 	parent_devlink = devlink_nl_ctx(info)->parent_devlink ? : devlink;	parent = devlink_rate->parent; 	if (parent && !len) {		if (devlink_rate_is_leaf(devlink_rate))			err = ops->rate_leaf_parent_set(devlink_rate, NULL,							devlink_rate->priv, NULL,							info->extack);		else if (devlink_rate_is_node(devlink_rate))			err = ops->rate_node_parent_set(devlink_rate, NULL,							devlink_rate->priv, NULL,							info->extack);		if (err)			return err; 		refcount_dec(&parent->refcnt);		devlink_rate->parent = NULL;	} else if (len) {		/* parent_devlink (when different than devlink) isn't locked,		 * but the rate node devlink instance is, so nobody from the		 * same group of devices sharing rates could change the used		 * fields or unregister the parent.		 */		parent = devlink_rate_node_get_by_name(rate_devlink,						       parent_devlink,						       parent_name);		if (IS_ERR(parent))			return -ENODEV; 		if (parent == devlink_rate) {			NL_SET_ERR_MSG(info->extack, "Parent to self is not allowed");			return -EINVAL;		} 		if (devlink_rate_is_node(devlink_rate) &&		    devlink_rate_is_parent_node(devlink_rate, parent->parent)) {			NL_SET_ERR_MSG(info->extack, "Node is already a parent of parent node.");			return -EEXIST;		} 		if (devlink_rate_is_leaf(devlink_rate))			err = ops->rate_leaf_parent_set(devlink_rate, parent,							devlink_rate->priv, parent->priv,							info->extack);		else if (devlink_rate_is_node(devlink_rate))			err = ops->rate_node_parent_set(devlink_rate, parent,							devlink_rate->priv, parent->priv,							info->extack);		if (err)			return err; 		if (devlink_rate->parent)			/* we're reassigning to other parent in this case */			refcount_dec(&devlink_rate->parent->refcnt); 		refcount_inc(&parent->refcnt);		devlink_rate->parent = parent;	} 	return 0;} static int devlink_nl_rate_tc_bw_parse(struct nlattr *parent_nest, u32 *tc_bw,				       unsigned long *bitmap,				       struct netlink_ext_ack *extack){	struct nlattr *tb[DEVLINK_RATE_TC_ATTR_MAX + 1];	u8 tc_index;	int err; 	err = nla_parse_nested(tb, DEVLINK_RATE_TC_ATTR_MAX, parent_nest,			       devlink_dl_rate_tc_bws_nl_policy, extack);	if (err)		return err; 	if (!tb[DEVLINK_RATE_TC_ATTR_INDEX]) {		NL_SET_ERR_ATTR_MISS(extack, parent_nest,				     DEVLINK_RATE_TC_ATTR_INDEX);		return -EINVAL;	} 	tc_index = nla_get_u8(tb[DEVLINK_RATE_TC_ATTR_INDEX]); 	if (!tb[DEVLINK_RATE_TC_ATTR_BW]) {		NL_SET_ERR_ATTR_MISS(extack, parent_nest,				     DEVLINK_RATE_TC_ATTR_BW);		return -EINVAL;	} 	if (test_and_set_bit(tc_index, bitmap)) {		NL_SET_ERR_MSG_FMT(extack,				   "Duplicate traffic class index specified (%u)",				   tc_index);		return -EINVAL;	} 	tc_bw[tc_index] = nla_get_u32(tb[DEVLINK_RATE_TC_ATTR_BW]); 	return 0;} static int devlink_nl_rate_tc_bw_set(struct devlink_rate *devlink_rate,				     struct genl_info *info){	DECLARE_BITMAP(bitmap, DEVLINK_RATE_TCS_MAX) = {};	struct devlink *devlink = devlink_rate->devlink;	const struct devlink_ops *ops = devlink->ops;	u32 tc_bw[DEVLINK_RATE_TCS_MAX] = {};	int rem, err = -EOPNOTSUPP, i;	struct nlattr *attr; 	nlmsg_for_each_attr_type(attr, DEVLINK_ATTR_RATE_TC_BWS, info->nlhdr,				 GENL_HDRLEN, rem) {		err = devlink_nl_rate_tc_bw_parse(attr, tc_bw, bitmap,						  info->extack);		if (err)			return err;	} 	for (i = 0; i < DEVLINK_RATE_TCS_MAX; i++) {		if (!test_bit(i, bitmap)) {			NL_SET_ERR_MSG_FMT(info->extack,					   "Bandwidth values must be specified for all %u traffic classes",

Lines 1–500 of 1031.