721 lines · 19.7 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;} 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);} static 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);} static 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 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)		if (nla_put_string(msg, DEVLINK_ATTR_RATE_PARENT_NODE_NAME,				   devlink_rate->parent->name))			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; 	list_for_each_entry(rate_node, &devlink->rate_list, list)		devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_NEW);} void devlink_rates_notify_unregister(struct devlink *devlink){	struct devlink_rate *rate_node; 	list_for_each_entry_reverse(rate_node, &devlink->rate_list, list)		devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_DEL);} 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;	int idx = 0;	int err = 0; 	list_for_each_entry(devlink_rate, &devlink->rate_list, list) {		enum devlink_command cmd = DEVLINK_CMD_RATE_NEW;		u32 id = NETLINK_CB(cb->skb).portid; 		if (idx < state->idx) {			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++;	} 	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 *devlink = info->user_ptr[0];	struct devlink_rate *devlink_rate;	struct sk_buff *msg;	int err; 	devlink_rate = devlink_rate_get_from_info(devlink, info);	if (IS_ERR(devlink_rate))		return PTR_ERR(devlink_rate); 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);	if (!msg)		return -ENOMEM; 	err = devlink_nl_rate_fill(msg, devlink_rate, DEVLINK_CMD_RATE_NEW,				   info->snd_portid, info->snd_seq, 0,				   info->extack);	if (err) {		nlmsg_free(msg);		return err;	} 	return genlmsg_reply(msg, info);} 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 genl_info *info,				struct nlattr *nla_parent){	struct devlink *devlink = devlink_rate->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_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_rate_node_get_by_name(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_set(struct devlink_rate *devlink_rate,			       const struct devlink_ops *ops,			       struct genl_info *info){	struct nlattr *nla_parent, **attrs = info->attrs;	int err = -EOPNOTSUPP;	u32 priority;	u32 weight;	u64 rate; 	if (attrs[DEVLINK_ATTR_RATE_TX_SHARE]) {		rate = nla_get_u64(attrs[DEVLINK_ATTR_RATE_TX_SHARE]);		if (devlink_rate_is_leaf(devlink_rate))			err = ops->rate_leaf_tx_share_set(devlink_rate, devlink_rate->priv,							  rate, info->extack);		else if (devlink_rate_is_node(devlink_rate))			err = ops->rate_node_tx_share_set(devlink_rate, devlink_rate->priv,							  rate, info->extack);		if (err)			return err;		devlink_rate->tx_share = rate;	} 	if (attrs[DEVLINK_ATTR_RATE_TX_MAX]) {		rate = nla_get_u64(attrs[DEVLINK_ATTR_RATE_TX_MAX]);		if (devlink_rate_is_leaf(devlink_rate))			err = ops->rate_leaf_tx_max_set(devlink_rate, devlink_rate->priv,							rate, info->extack);		else if (devlink_rate_is_node(devlink_rate))			err = ops->rate_node_tx_max_set(devlink_rate, devlink_rate->priv,							rate, info->extack);		if (err)			return err;		devlink_rate->tx_max = rate;	} 	if (attrs[DEVLINK_ATTR_RATE_TX_PRIORITY]) {		priority = nla_get_u32(attrs[DEVLINK_ATTR_RATE_TX_PRIORITY]);		if (devlink_rate_is_leaf(devlink_rate))			err = ops->rate_leaf_tx_priority_set(devlink_rate, devlink_rate->priv,							     priority, info->extack);		else if (devlink_rate_is_node(devlink_rate))			err = ops->rate_node_tx_priority_set(devlink_rate, devlink_rate->priv,							     priority, info->extack); 		if (err)			return err;		devlink_rate->tx_priority = priority;	} 	if (attrs[DEVLINK_ATTR_RATE_TX_WEIGHT]) {		weight = nla_get_u32(attrs[DEVLINK_ATTR_RATE_TX_WEIGHT]);		if (devlink_rate_is_leaf(devlink_rate))			err = ops->rate_leaf_tx_weight_set(devlink_rate, devlink_rate->priv,							   weight, info->extack);		else if (devlink_rate_is_node(devlink_rate))			err = ops->rate_node_tx_weight_set(devlink_rate, devlink_rate->priv,							   weight, info->extack); 		if (err)			return err;		devlink_rate->tx_weight = weight;	} 	nla_parent = attrs[DEVLINK_ATTR_RATE_PARENT_NODE_NAME];	if (nla_parent) {		err = devlink_nl_rate_parent_node_set(devlink_rate, info,						      nla_parent);		if (err)			return err;	} 	return 0;} static bool devlink_rate_set_ops_supported(const struct devlink_ops *ops,					   struct genl_info *info,					   enum devlink_rate_type type){	struct nlattr **attrs = info->attrs; 	if (type == DEVLINK_RATE_TYPE_LEAF) {		if (attrs[DEVLINK_ATTR_RATE_TX_SHARE] && !ops->rate_leaf_tx_share_set) {			NL_SET_ERR_MSG(info->extack, "TX share set isn't supported for the leafs");			return false;		}		if (attrs[DEVLINK_ATTR_RATE_TX_MAX] && !ops->rate_leaf_tx_max_set) {			NL_SET_ERR_MSG(info->extack, "TX max set isn't supported for the leafs");			return false;		}		if (attrs[DEVLINK_ATTR_RATE_PARENT_NODE_NAME] &&		    !ops->rate_leaf_parent_set) {			NL_SET_ERR_MSG(info->extack, "Parent set isn't supported for the leafs");			return false;		}		if (attrs[DEVLINK_ATTR_RATE_TX_PRIORITY] && !ops->rate_leaf_tx_priority_set) {			NL_SET_ERR_MSG_ATTR(info->extack,					    attrs[DEVLINK_ATTR_RATE_TX_PRIORITY],					    "TX priority set isn't supported for the leafs");			return false;		}		if (attrs[DEVLINK_ATTR_RATE_TX_WEIGHT] && !ops->rate_leaf_tx_weight_set) {			NL_SET_ERR_MSG_ATTR(info->extack,					    attrs[DEVLINK_ATTR_RATE_TX_WEIGHT],					    "TX weight set isn't supported for the leafs");			return false;		}	} else if (type == DEVLINK_RATE_TYPE_NODE) {		if (attrs[DEVLINK_ATTR_RATE_TX_SHARE] && !ops->rate_node_tx_share_set) {			NL_SET_ERR_MSG(info->extack, "TX share set isn't supported for the nodes");			return false;		}		if (attrs[DEVLINK_ATTR_RATE_TX_MAX] && !ops->rate_node_tx_max_set) {			NL_SET_ERR_MSG(info->extack, "TX max set isn't supported for the nodes");			return false;		}		if (attrs[DEVLINK_ATTR_RATE_PARENT_NODE_NAME] &&		    !ops->rate_node_parent_set) {			NL_SET_ERR_MSG(info->extack, "Parent set isn't supported for the nodes");			return false;		}		if (attrs[DEVLINK_ATTR_RATE_TX_PRIORITY] && !ops->rate_node_tx_priority_set) {			NL_SET_ERR_MSG_ATTR(info->extack,					    attrs[DEVLINK_ATTR_RATE_TX_PRIORITY],					    "TX priority set isn't supported for the nodes");			return false;		}		if (attrs[DEVLINK_ATTR_RATE_TX_WEIGHT] && !ops->rate_node_tx_weight_set) {			NL_SET_ERR_MSG_ATTR(info->extack,					    attrs[DEVLINK_ATTR_RATE_TX_WEIGHT],					    "TX weight set isn't supported for the nodes");			return false;		}	} else {		WARN(1, "Unknown type of rate object");		return false;	} 	return true;} int devlink_nl_rate_set_doit(struct sk_buff *skb, struct genl_info *info){	struct devlink *devlink = info->user_ptr[0];	struct devlink_rate *devlink_rate;	const struct devlink_ops *ops;	int err; 	devlink_rate = devlink_rate_get_from_info(devlink, info);	if (IS_ERR(devlink_rate))		return PTR_ERR(devlink_rate); 	ops = devlink->ops;	if (!ops || !devlink_rate_set_ops_supported(ops, info, devlink_rate->type))		return -EOPNOTSUPP; 	err = devlink_nl_rate_set(devlink_rate, ops, info); 	if (!err)		devlink_rate_notify(devlink_rate, DEVLINK_CMD_RATE_NEW);	return err;} int devlink_nl_rate_new_doit(struct sk_buff *skb, struct genl_info *info){	struct devlink *devlink = info->user_ptr[0];	struct devlink_rate *rate_node;	const struct devlink_ops *ops;	int err; 	ops = devlink->ops;	if (!ops || !ops->rate_node_new || !ops->rate_node_del) {		NL_SET_ERR_MSG(info->extack, "Rate nodes aren't supported");		return -EOPNOTSUPP;	} 	if (!devlink_rate_set_ops_supported(ops, info, DEVLINK_RATE_TYPE_NODE))		return -EOPNOTSUPP; 	rate_node = devlink_rate_node_get_from_attrs(devlink, info->attrs);	if (!IS_ERR(rate_node))		return -EEXIST;

Lines 1–500 of 721.