@@ -48,6 +48,18 @@ static bool is_detached(u32 handle)
return net_shaper_handle_scope(handle) == NET_SHAPER_SCOPE_DETACHED;
}
+/* count the number of [multi] attributes of the given type */
+static int attr_list_len(struct genl_info *info, int type)
+{
+ struct nlattr *attr;
+ int rem, cnt = 0;
+
+ nla_for_each_attr_type(attr, type, genlmsg_data(info->genlhdr),
+ genlmsg_len(info->genlhdr), rem)
+ cnt++;
+ return cnt;
+}
+
static int fill_handle(struct sk_buff *msg, u32 handle, u32 type,
const struct genl_info *info)
{
@@ -255,6 +267,27 @@ static void sc_commit(struct net_device *dev, int nr_shapers,
xa_unlock(xa);
}
+/* rollback all the tentative inserts from the shaper cache */
+static void sc_rollback(struct net_device *dev)
+{
+ struct xarray *xa = __sc_container(dev);
+ struct net_shaper_info *cur;
+ unsigned long index;
+
+ if (!xa)
+ return;
+
+ xa_lock(xa);
+ xa_for_each_marked(xa, index, cur, XA_MARK_0) {
+ if (is_detached(index))
+ idr_remove(&dev->net_shaper_data->detached_ids,
+ net_shaper_handle_id(index));
+ __xa_erase(xa, index);
+ kfree(cur);
+ }
+ xa_unlock(xa);
+}
+
static int parse_handle(const struct nlattr *attr, const struct genl_info *info,
u32 *handle)
{
@@ -354,6 +387,36 @@ static int parse_shaper(struct net_device *dev, const struct nlattr *attr,
return __parse_shaper(dev, tb, info, shaper);
}
+/* alike parse_shaper(), but additionally allow the user specifying
+ * the shaper's parent handle
+ */
+static int parse_output_shaper(struct net_device *dev,
+ const struct nlattr *attr,
+ const struct genl_info *info,
+ struct net_shaper_info *shaper)
+{
+ struct nlattr *tb[NET_SHAPER_A_PARENT + 1];
+ int ret;
+
+ ret = nla_parse_nested(tb, NET_SHAPER_A_PARENT, attr,
+ net_shaper_ns_output_info_nl_policy,
+ info->extack);
+ if (ret < 0)
+ return ret;
+
+ ret = __parse_shaper(dev, tb, info, shaper);
+ if (ret)
+ return ret;
+
+ if (tb[NET_SHAPER_A_PARENT]) {
+ ret = parse_handle(tb[NET_SHAPER_A_PARENT], info,
+ &shaper->parent);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
int net_shaper_nl_get_doit(struct sk_buff *skb, struct genl_info *info)
{
struct net_shaper_info *shaper;
@@ -569,9 +632,209 @@ int net_shaper_nl_delete_doit(struct sk_buff *skb, struct genl_info *info)
return ret;
}
+/* Update the H/W and on success update the local cache, too */
+static int net_shaper_group(struct net_device *dev, int nr_inputs,
+ struct net_shaper_info *inputs,
+ struct net_shaper_info *output,
+ struct netlink_ext_ack *extack)
+{
+ enum net_shaper_scope scope, output_scope, output_pscope;
+ struct net_shaper_info *parent = NULL;
+ int i, ret;
+
+ output_scope = net_shaper_handle_scope(output->handle);
+ if (output_scope != NET_SHAPER_SCOPE_DETACHED &&
+ output_scope != NET_SHAPER_SCOPE_NETDEV) {
+ NL_SET_ERR_MSG_FMT(extack, "Invalid scope %d for output shaper %x",
+ output_scope, output->handle);
+ return -EINVAL;
+ }
+
+ output_pscope = net_shaper_handle_scope(output->parent);
+ if (output_scope == NET_SHAPER_SCOPE_DETACHED) {
+ if (net_shaper_handle_id(output->handle) !=
+ NET_SHAPER_ID_UNSPEC && !sc_lookup(dev, output->handle)) {
+ NL_SET_ERR_MSG_FMT(extack, "Output shaper %x does not exists",
+ output->handle);
+ return -EINVAL;
+ }
+ if (output_pscope != NET_SHAPER_SCOPE_DETACHED &&
+ output_pscope != NET_SHAPER_SCOPE_NETDEV) {
+ NL_SET_ERR_MSG_FMT(extack, "Invalid scope %d for output parent shaper %x",
+ output_pscope, output->parent);
+ return -EINVAL;
+ }
+ }
+
+ if (output_pscope == NET_SHAPER_SCOPE_DETACHED) {
+ parent = sc_lookup(dev, output->parent);
+ if (!parent) {
+ NL_SET_ERR_MSG_FMT(extack, "Output parent shaper %x does not exists",
+ output->parent);
+ return -EINVAL;
+ }
+ }
+
+ /* for newly created detached scope shaper, the following will update
+ * the handle, due to id allocation
+ */
+ ret = sc_prepare_insert(dev, &output->handle, extack);
+ if (ret)
+ goto rollback;
+
+ for (i = 0; i < nr_inputs; ++i) {
+ scope = net_shaper_handle_scope(inputs[i].handle);
+ if (scope != NET_SHAPER_SCOPE_QUEUE &&
+ scope != NET_SHAPER_SCOPE_DETACHED) {
+ ret = -EINVAL;
+ NL_SET_ERR_MSG_FMT(extack, "Invalid scope %d for input shaper %d handle %x",
+ scope, i, inputs[i].handle);
+ goto rollback;
+ }
+ if (scope == NET_SHAPER_SCOPE_DETACHED &&
+ !sc_lookup(dev, inputs[i].handle)) {
+ ret = -EINVAL;
+ NL_SET_ERR_MSG_FMT(extack,
+ "Can't create detached shaper as input %d handle %x",
+ i, inputs[i].handle);
+ goto rollback;
+ }
+
+ ret = sc_prepare_insert(dev, &inputs[i].handle, extack);
+ if (ret)
+ goto rollback;
+
+ /* the inputs shapers will be nested to the output */
+ if (inputs[i].parent != output->handle) {
+ inputs[i].parent = output->handle;
+ output->children++;
+ }
+ }
+
+ ret = dev->netdev_ops->net_shaper_ops->group(dev, nr_inputs,
+ inputs, output,
+ extack);
+ if (ret < 0)
+ goto rollback;
+
+ if (parent)
+ parent->children++;
+ sc_commit(dev, 1, output);
+ sc_commit(dev, nr_inputs, inputs);
+ return ret;
+
+rollback:
+ sc_rollback(dev);
+ return ret;
+}
+
+static int nla_handle_total_size(void)
+{
+ return nla_total_size(nla_total_size(sizeof(u32)) +
+ nla_total_size(sizeof(u32)));
+}
+
+static int group_send_reply(struct genl_info *info, u32 handle, int id)
+{
+ struct nlattr *handle_attr;
+ struct sk_buff *msg;
+ int ret = -EMSGSIZE;
+ void *hdr;
+
+/* prepare the msg reply in advance, to avoid device operation rollback */
+ msg = genlmsg_new(nla_handle_total_size(), GFP_KERNEL);
+ if (!msg)
+ return ret;
+
+ hdr = genlmsg_iput(msg, info);
+ if (!hdr)
+ goto free_msg;
+
+ handle_attr = nla_nest_start(msg, NET_SHAPER_A_HANDLE);
+ if (!handle_attr)
+ goto free_msg;
+
+ if (nla_put_u32(msg, NET_SHAPER_A_SCOPE,
+ net_shaper_handle_scope(handle)))
+ goto free_msg;
+
+ if (nla_put_u32(msg, NET_SHAPER_A_ID, id))
+ goto free_msg;
+
+ nla_nest_end(msg, handle_attr);
+ genlmsg_end(msg, hdr);
+
+ ret = genlmsg_reply(msg, info);
+ if (ret)
+ goto free_msg;
+
+ return ret;
+
+free_msg:
+ nlmsg_free(msg);
+ return ret;
+}
+
int net_shaper_nl_group_doit(struct sk_buff *skb, struct genl_info *info)
{
- return -EOPNOTSUPP;
+ struct net_shaper_info *inputs, output;
+ int i, ret, rem, nr_inputs;
+ struct net_device *dev;
+ struct nlattr *attr;
+
+ if (GENL_REQ_ATTR_CHECK(info, NET_SHAPER_A_INPUTS) ||
+ GENL_REQ_ATTR_CHECK(info, NET_SHAPER_A_OUTPUT))
+ return -EINVAL;
+
+ ret = fetch_dev(info, &dev);
+ if (ret)
+ return ret;
+
+ nr_inputs = attr_list_len(info, NET_SHAPER_A_INPUTS);
+ inputs = kcalloc(nr_inputs, sizeof(struct net_shaper_info), GFP_KERNEL);
+ if (!inputs) {
+ GENL_SET_ERR_MSG_FMT(info, "Can't allocate memory for %d input shapers",
+ nr_inputs);
+ ret = -ENOMEM;
+ goto put;
+ }
+
+ ret = parse_output_shaper(dev, info->attrs[NET_SHAPER_A_OUTPUT], info,
+ &output);
+ if (ret)
+ goto free_shapers;
+
+ i = 0;
+ nla_for_each_attr_type(attr, NET_SHAPER_A_INPUTS,
+ genlmsg_data(info->genlhdr),
+ genlmsg_len(info->genlhdr), rem) {
+ if (WARN_ON_ONCE(i >= nr_inputs))
+ goto free_shapers;
+
+ ret = parse_shaper(dev, attr, info, &inputs[i++]);
+ if (ret)
+ goto free_shapers;
+ }
+
+ ret = net_shaper_group(dev, nr_inputs, inputs, &output, info->extack);
+ if (ret < 0)
+ goto free_shapers;
+
+ ret = group_send_reply(info, output.handle, ret);
+ if (ret) {
+ /* Error on reply is not fatal to avoid rollback a successful
+ * configuration
+ */
+ GENL_SET_ERR_MSG_FMT(info, "Can't send reply %d", ret);
+ ret = 0;
+ }
+
+free_shapers:
+ kfree(inputs);
+
+put:
+ netdev_put(dev, NULL);
+ return ret;
}
void dev_shaper_flush(struct net_device *dev)
Allow grouping multiple inputs shaper under the given output's one. Try hard to pre-allocated the needed resources, to avoid non trivial H/W configuration rollbacks in case of any failure. Signed-off-by: Paolo Abeni <pabeni@redhat.com> --- RFC v2 -> RFC v3: - dev_put() -> netdev_put() --- net/shaper/shaper.c | 265 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 264 insertions(+), 1 deletion(-)