mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2024-12-28 00:33:16 +00:00
net-shapers: implement cap validation in the core
Use the device capabilities to reject invalid attribute values before pushing them to the H/W. Note that validating the metric explicitly avoids NL_SET_BAD_ATTR() usage, to provide unambiguous error messages to the user. Validating the nesting requires the knowledge of the new parent for the given shaper; as such is a chicken-egg problem: to validate the leaf nesting we need to know the node scope, to validate the node nesting we need to know the leafs parent scope. To break the circular dependency, place the leafs nesting validation after the parsing. Suggested-by: Jakub Kicinski <kuba@kernel.org> Reviewed-by: Jakub Kicinski <kuba@kernel.org> Reviewed-by: Jiri Pirko <jiri@nvidia.com> Signed-off-by: Paolo Abeni <pabeni@redhat.com> Link: https://patch.msgid.link/54667601813e4c0348f39bf8ad2446ffc9fcd383.1728460186.git.pabeni@redhat.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
553ea9f1ef
commit
ecd82cfee3
@ -439,6 +439,74 @@ static int net_shaper_parse_handle(const struct nlattr *attr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int net_shaper_validate_caps(struct net_shaper_binding *binding,
|
||||
struct nlattr **tb,
|
||||
const struct genl_info *info,
|
||||
struct net_shaper *shaper)
|
||||
{
|
||||
const struct net_shaper_ops *ops = net_shaper_ops(binding);
|
||||
struct nlattr *bad = NULL;
|
||||
unsigned long caps = 0;
|
||||
|
||||
ops->capabilities(binding, shaper->handle.scope, &caps);
|
||||
|
||||
if (tb[NET_SHAPER_A_PRIORITY] &&
|
||||
!(caps & BIT(NET_SHAPER_A_CAPS_SUPPORT_PRIORITY)))
|
||||
bad = tb[NET_SHAPER_A_PRIORITY];
|
||||
if (tb[NET_SHAPER_A_WEIGHT] &&
|
||||
!(caps & BIT(NET_SHAPER_A_CAPS_SUPPORT_WEIGHT)))
|
||||
bad = tb[NET_SHAPER_A_WEIGHT];
|
||||
if (tb[NET_SHAPER_A_BW_MIN] &&
|
||||
!(caps & BIT(NET_SHAPER_A_CAPS_SUPPORT_BW_MIN)))
|
||||
bad = tb[NET_SHAPER_A_BW_MIN];
|
||||
if (tb[NET_SHAPER_A_BW_MAX] &&
|
||||
!(caps & BIT(NET_SHAPER_A_CAPS_SUPPORT_BW_MAX)))
|
||||
bad = tb[NET_SHAPER_A_BW_MAX];
|
||||
if (tb[NET_SHAPER_A_BURST] &&
|
||||
!(caps & BIT(NET_SHAPER_A_CAPS_SUPPORT_BURST)))
|
||||
bad = tb[NET_SHAPER_A_BURST];
|
||||
|
||||
if (!caps)
|
||||
bad = tb[NET_SHAPER_A_HANDLE];
|
||||
|
||||
if (bad) {
|
||||
NL_SET_BAD_ATTR(info->extack, bad);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (shaper->handle.scope == NET_SHAPER_SCOPE_QUEUE &&
|
||||
binding->type == NET_SHAPER_BINDING_TYPE_NETDEV &&
|
||||
shaper->handle.id >= binding->netdev->real_num_tx_queues) {
|
||||
NL_SET_ERR_MSG_FMT(info->extack,
|
||||
"Not existing queue id %d max %d",
|
||||
shaper->handle.id,
|
||||
binding->netdev->real_num_tx_queues);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/* The metric is really used only if there is *any* rate-related
|
||||
* setting, either in current attributes set or in pre-existing
|
||||
* values.
|
||||
*/
|
||||
if (shaper->burst || shaper->bw_min || shaper->bw_max) {
|
||||
u32 metric_cap = NET_SHAPER_A_CAPS_SUPPORT_METRIC_BPS +
|
||||
shaper->metric;
|
||||
|
||||
/* The metric test can fail even when the user did not
|
||||
* specify the METRIC attribute. Pointing to rate related
|
||||
* attribute will be confusing, as the attribute itself
|
||||
* could be indeed supported, with a different metric.
|
||||
* Be more specific.
|
||||
*/
|
||||
if (!(caps & BIT(metric_cap))) {
|
||||
NL_SET_ERR_MSG_FMT(info->extack, "Bad metric %d",
|
||||
shaper->metric);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int net_shaper_parse_info(struct net_shaper_binding *binding,
|
||||
struct nlattr **tb,
|
||||
const struct genl_info *info,
|
||||
@ -487,6 +555,28 @@ static int net_shaper_parse_info(struct net_shaper_binding *binding,
|
||||
|
||||
if (tb[NET_SHAPER_A_WEIGHT])
|
||||
shaper->weight = nla_get_u32(tb[NET_SHAPER_A_WEIGHT]);
|
||||
|
||||
ret = net_shaper_validate_caps(binding, tb, info, shaper);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int net_shaper_validate_nesting(struct net_shaper_binding *binding,
|
||||
const struct net_shaper *shaper,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
const struct net_shaper_ops *ops = net_shaper_ops(binding);
|
||||
unsigned long caps = 0;
|
||||
|
||||
ops->capabilities(binding, shaper->handle.scope, &caps);
|
||||
if (!(caps & BIT(NET_SHAPER_A_CAPS_SUPPORT_NESTING))) {
|
||||
NL_SET_ERR_MSG_FMT(extack,
|
||||
"Nesting not supported for scope %d",
|
||||
shaper->handle.scope);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -517,6 +607,13 @@ static int net_shaper_parse_leaf(struct net_shaper_binding *binding,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (node->handle.scope == NET_SHAPER_SCOPE_NODE) {
|
||||
ret = net_shaper_validate_nesting(binding, shaper,
|
||||
info->extack);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!exists)
|
||||
net_shaper_default_parent(&shaper->handle, &shaper->parent);
|
||||
return 0;
|
||||
@ -858,6 +955,10 @@ static int __net_shaper_group(struct net_shaper_binding *binding,
|
||||
node->parent.scope, node->parent.id);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
ret = net_shaper_validate_nesting(binding, node, extack);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (update_node) {
|
||||
|
Loading…
Reference in New Issue
Block a user