@@ -874,8 +874,8 @@ int mlx5_esw_devlink_rate_node_tx_max_set(struct devlink_rate *rate_node, void *
int mlx5_esw_devlink_rate_node_new(struct devlink_rate *rate_node, void **priv,
struct netlink_ext_ack *extack)
{
- struct mlx5_esw_rate_group *group;
struct mlx5_eswitch *esw;
+ void *group;
int err = 0;
esw = mlx5_devlink_eswitch_get(rate_node->devlink);
@@ -890,7 +890,17 @@ int mlx5_esw_devlink_rate_node_new(struct devlink_rate *rate_node, void **priv,
goto unlock;
}
- group = esw_qos_create_rate_group(esw, extack);
+ switch (rate_node->limit_type) {
+ case DEVLINK_RATE_LIMIT_TYPE_UNSET:
+ group = ERR_PTR(-EINVAL);
+ break;
+ case DEVLINK_RATE_LIMIT_TYPE_SHAPING:
+ group = esw_qos_create_rate_group(esw, extack);
+ break;
+ default:
+ group = ERR_PTR(-EOPNOTSUPP);
+ }
+
if (IS_ERR(group)) {
err = PTR_ERR(group);
goto unlock;
@@ -905,7 +915,6 @@ int mlx5_esw_devlink_rate_node_new(struct devlink_rate *rate_node, void **priv,
int mlx5_esw_devlink_rate_node_del(struct devlink_rate *rate_node, void *priv,
struct netlink_ext_ack *extack)
{
- struct mlx5_esw_rate_group *group = priv;
struct mlx5_eswitch *esw;
int err;
@@ -914,7 +923,18 @@ int mlx5_esw_devlink_rate_node_del(struct devlink_rate *rate_node, void *priv,
return PTR_ERR(esw);
mutex_lock(&esw->state_lock);
- err = esw_qos_destroy_rate_group(esw, group, extack);
+
+ switch (rate_node->limit_type) {
+ case DEVLINK_RATE_LIMIT_TYPE_UNSET:
+ err = -EINVAL;
+ break;
+ case DEVLINK_RATE_LIMIT_TYPE_SHAPING:
+ err = esw_qos_destroy_rate_group(esw, priv, extack);
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ }
+
mutex_unlock(&esw->state_lock);
return err;
}
@@ -98,13 +98,21 @@ struct devlink_port_attrs {
};
};
+struct devlink_rate_shaping_attrs {
+ u64 tx_max;
+ u64 tx_share;
+};
+
struct devlink_rate {
struct list_head list;
+ enum devlink_rate_limit_type limit_type;
enum devlink_rate_type type;
struct devlink *devlink;
void *priv;
- u64 tx_share;
- u64 tx_max;
+
+ union { /* on limit_type */
+ struct devlink_rate_shaping_attrs shaping_attrs;
+ };
struct devlink_rate *parent;
union {
@@ -221,6 +221,11 @@ enum devlink_rate_type {
DEVLINK_RATE_TYPE_NODE,
};
+enum devlink_rate_limit_type {
+ DEVLINK_RATE_LIMIT_TYPE_UNSET,
+ DEVLINK_RATE_LIMIT_TYPE_SHAPING,
+};
+
enum devlink_param_cmode {
DEVLINK_PARAM_CMODE_RUNTIME,
DEVLINK_PARAM_CMODE_DRIVERINIT,
@@ -576,6 +581,8 @@ enum devlink_attr {
DEVLINK_ATTR_LINECARD_TYPE, /* string */
DEVLINK_ATTR_LINECARD_SUPPORTED_TYPES, /* nested */
+ DEVLINK_ATTR_RATE_LIMIT_TYPE, /* u16 */
+
/* add new attributes above here, update the policy in devlink.c */
__DEVLINK_ATTR_MAX,
@@ -354,6 +354,18 @@ devlink_rate_is_node(struct devlink_rate *devlink_rate)
return devlink_rate->type == DEVLINK_RATE_TYPE_NODE;
}
+static inline bool
+devlink_rate_is_unset(struct devlink_rate *devlink_rate)
+{
+ return devlink_rate->limit_type == DEVLINK_RATE_LIMIT_TYPE_UNSET;
+}
+
+static inline bool
+devlink_rate_is_shaping(struct devlink_rate *devlink_rate)
+{
+ return devlink_rate->limit_type == DEVLINK_RATE_LIMIT_TYPE_SHAPING;
+}
+
static struct devlink_rate *
devlink_rate_leaf_get_from_info(struct devlink *devlink, struct genl_info *info)
{
@@ -1093,13 +1105,27 @@ static int devlink_nl_rate_fill(struct sk_buff *msg,
goto nla_put_failure;
}
- if (nla_put_u64_64bit(msg, DEVLINK_ATTR_RATE_TX_SHARE,
- devlink_rate->tx_share, DEVLINK_ATTR_PAD))
+ if (nla_put_u64_64bit(msg, DEVLINK_ATTR_RATE_LIMIT_TYPE,
+ devlink_rate->limit_type, DEVLINK_ATTR_PAD))
goto nla_put_failure;
- if (nla_put_u64_64bit(msg, DEVLINK_ATTR_RATE_TX_MAX,
- devlink_rate->tx_max, DEVLINK_ATTR_PAD))
- goto nla_put_failure;
+ if (devlink_rate_is_unset(devlink_rate)) {
+ /* For backward compatibility with older user-space clients that
+ * don't understatnd DEVLINK_ATTR_RATE_LIMIT_TYPE, report tx_max
+ * and tx_share as being "unlimited".
+ */
+ if (nla_put_u64_64bit(msg, DEVLINK_ATTR_RATE_TX_MAX, 0, DEVLINK_ATTR_PAD))
+ goto nla_put_failure;
+ if (nla_put_u64_64bit(msg, DEVLINK_ATTR_RATE_TX_SHARE, 0, DEVLINK_ATTR_PAD))
+ goto nla_put_failure;
+ } else if (devlink_rate_is_shaping(devlink_rate)) {
+ if (nla_put_u64_64bit(msg, DEVLINK_ATTR_RATE_TX_MAX,
+ devlink_rate->shaping_attrs.tx_max, DEVLINK_ATTR_PAD))
+ goto nla_put_failure;
+ if (nla_put_u64_64bit(msg, DEVLINK_ATTR_RATE_TX_SHARE,
+ devlink_rate->shaping_attrs.tx_share, DEVLINK_ATTR_PAD))
+ goto nla_put_failure;
+ }
if (devlink_rate->parent)
if (nla_put_string(msg, DEVLINK_ATTR_RATE_PARENT_NODE_NAME,
@@ -1850,6 +1876,12 @@ devlink_nl_rate_parent_node_set(struct devlink_rate *devlink_rate,
return -EEXIST;
}
+ if (parent->limit_type != devlink_rate->limit_type) {
+ NL_SET_ERR_MSG_MOD(info->extack,
+ "Parent and object should be of the same limit_type");
+ return -EINVAL;
+ }
+
if (devlink_rate_is_leaf(devlink_rate))
err = ops->rate_leaf_parent_set(devlink_rate, parent,
devlink_rate->priv, parent->priv,
@@ -1873,44 +1905,82 @@ static int devlink_nl_rate_set(struct devlink_rate *devlink_rate,
struct genl_info *info)
{
struct nlattr *nla_parent, **attrs = info->attrs;
- int err = -EOPNOTSUPP;
- u64 rate;
+ struct devlink_rate *parent;
+ int err = 0;
+ u16 new_limit_type;
+ u64 new_val;
+
+ nla_parent = attrs[DEVLINK_ATTR_RATE_PARENT_NODE_NAME];
+
+ if (attrs[DEVLINK_ATTR_RATE_LIMIT_TYPE]) {
+ new_limit_type = nla_get_u16(attrs[DEVLINK_ATTR_RATE_LIMIT_TYPE]);
+ if (devlink_rate_is_unset(devlink_rate))
+ devlink_rate->limit_type = new_limit_type;
+ if (devlink_rate->limit_type != new_limit_type) {
+ if (devlink_rate_is_node(devlink_rate)) {
+ NL_SET_ERR_MSG_MOD(info->extack,
+ "Cannot change limit_type of the rate node object, delete and add a new one instead.");
+ return -EINVAL;
+ }
+ NL_SET_ERR_MSG_MOD(info->extack,
+ "Cannot change limit_type of the rate leaf object, reset current rate attributes first.");
+ return -EBUSY;
+ }
+ }
+
+ if (devlink_rate_is_unset(devlink_rate)) {
+ if (nla_parent) {
+ parent = devlink_rate_node_get_by_name(devlink_rate->devlink,
+ nla_data(nla_parent));
+ if (!IS_ERR(parent))
+ devlink_rate->limit_type = parent->limit_type;
+ else
+ devlink_rate->limit_type = DEVLINK_RATE_LIMIT_TYPE_SHAPING;
+ } else {
+ devlink_rate->limit_type = DEVLINK_RATE_LIMIT_TYPE_SHAPING;
+ }
+ }
+
+ if (attrs[DEVLINK_ATTR_RATE_TX_SHARE] && devlink_rate_is_shaping(devlink_rate)) {
+ new_val = nla_get_u64(attrs[DEVLINK_ATTR_RATE_TX_SHARE]);
- 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);
+ new_val, 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);
+ new_val, info->extack);
if (err)
return err;
- devlink_rate->tx_share = rate;
+ devlink_rate->shaping_attrs.tx_share = new_val;
}
if (attrs[DEVLINK_ATTR_RATE_TX_MAX]) {
- rate = nla_get_u64(attrs[DEVLINK_ATTR_RATE_TX_MAX]);
+ new_val = 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);
+ new_val, 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);
+ new_val, info->extack);
if (err)
return err;
- devlink_rate->tx_max = rate;
+ devlink_rate->shaping_attrs.tx_max = new_val;
}
- 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;
- }
+ if (nla_parent)
+ err = devlink_nl_rate_parent_node_set(devlink_rate, info, nla_parent);
- return 0;
+ /* reset limit_type when all attrs have been cleared, relevant only for
+ * leaf objects as node objects get deleted altogether
+ */
+ if (devlink_rate_is_leaf(devlink_rate) && !devlink_rate->parent &&
+ ((devlink_rate_is_shaping(devlink_rate) &&
+ !devlink_rate->shaping_attrs.tx_max && !devlink_rate->shaping_attrs.tx_share)))
+ devlink_rate->limit_type = DEVLINK_RATE_LIMIT_TYPE_UNSET;
+
+ return err;
}
static bool devlink_rate_set_ops_supported(const struct devlink_ops *ops,
@@ -2002,6 +2072,10 @@ static int devlink_nl_cmd_rate_new_doit(struct sk_buff *skb,
rate_node->devlink = devlink;
rate_node->type = DEVLINK_RATE_TYPE_NODE;
+ if (info->attrs[DEVLINK_ATTR_RATE_LIMIT_TYPE])
+ rate_node->limit_type = nla_get_u16(info->attrs[DEVLINK_ATTR_RATE_LIMIT_TYPE]);
+ if (rate_node->limit_type == DEVLINK_RATE_LIMIT_TYPE_UNSET)
+ rate_node->limit_type = DEVLINK_RATE_LIMIT_TYPE_SHAPING;
rate_node->name = nla_strdup(info->attrs[DEVLINK_ATTR_RATE_NODE_NAME], GFP_KERNEL);
if (!rate_node->name) {
err = -ENOMEM;
@@ -9000,6 +9074,7 @@ static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {
[DEVLINK_ATTR_RATE_PARENT_NODE_NAME] = { .type = NLA_NUL_STRING },
[DEVLINK_ATTR_LINECARD_INDEX] = { .type = NLA_U32 },
[DEVLINK_ATTR_LINECARD_TYPE] = { .type = NLA_NUL_STRING },
+ [DEVLINK_ATTR_RATE_LIMIT_TYPE] = { .type = NLA_U16 },
};
static const struct genl_small_ops devlink_nl_ops[] = {
Lay foundation to support different kinds of rate limiting that may be performed by rate objects. Existing rate limiting type is dubbed as 'shaping' and it is assumed by default when limit_type attribute isn't set explicitly. Following patch in the series will introduce new limit type 'police'. Leaf rate objects inherit their limit_type from a parent node object if it hasn't been explicitly set for the leaf object. Signed-off-by: Dima Chumak <dchumak@nvidia.com> --- .../net/ethernet/mellanox/mlx5/core/esw/qos.c | 28 +++- include/net/devlink.h | 12 +- include/uapi/linux/devlink.h | 7 + net/core/devlink.c | 123 ++++++++++++++---- 4 files changed, 140 insertions(+), 30 deletions(-)