// 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 bool
devlink_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++