Merge tag 'mt76-for-kvalo-2021-01-29' of https://github.com/nbd168/wireless
[linux-2.6-microblaze.git] / net / sched / act_api.c
index 2e85b63..4dd235c 100644 (file)
@@ -928,19 +928,13 @@ static void tcf_idr_insert_many(struct tc_action *actions[])
        }
 }
 
-struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
-                                   struct nlattr *nla, struct nlattr *est,
-                                   char *name, int ovr, int bind,
-                                   bool rtnl_held,
-                                   struct netlink_ext_ack *extack)
+struct tc_action_ops *tc_action_load_ops(char *name, struct nlattr *nla,
+                                        bool rtnl_held,
+                                        struct netlink_ext_ack *extack)
 {
-       struct nla_bitfield32 flags = { 0, 0 };
-       u8 hw_stats = TCA_ACT_HW_STATS_ANY;
-       struct tc_action *a;
+       struct nlattr *tb[TCA_ACT_MAX + 1];
        struct tc_action_ops *a_o;
-       struct tc_cookie *cookie = NULL;
        char act_name[IFNAMSIZ];
-       struct nlattr *tb[TCA_ACT_MAX + 1];
        struct nlattr *kind;
        int err;
 
@@ -948,33 +942,21 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
                err = nla_parse_nested_deprecated(tb, TCA_ACT_MAX, nla,
                                                  tcf_action_policy, extack);
                if (err < 0)
-                       goto err_out;
+                       return ERR_PTR(err);
                err = -EINVAL;
                kind = tb[TCA_ACT_KIND];
                if (!kind) {
                        NL_SET_ERR_MSG(extack, "TC action kind must be specified");
-                       goto err_out;
+                       return ERR_PTR(err);
                }
                if (nla_strscpy(act_name, kind, IFNAMSIZ) < 0) {
                        NL_SET_ERR_MSG(extack, "TC action name too long");
-                       goto err_out;
-               }
-               if (tb[TCA_ACT_COOKIE]) {
-                       cookie = nla_memdup_cookie(tb);
-                       if (!cookie) {
-                               NL_SET_ERR_MSG(extack, "No memory to generate TC cookie");
-                               err = -ENOMEM;
-                               goto err_out;
-                       }
+                       return ERR_PTR(err);
                }
-               hw_stats = tcf_action_hw_stats_get(tb[TCA_ACT_HW_STATS]);
-               if (tb[TCA_ACT_FLAGS])
-                       flags = nla_get_bitfield32(tb[TCA_ACT_FLAGS]);
        } else {
                if (strlcpy(act_name, name, IFNAMSIZ) >= IFNAMSIZ) {
                        NL_SET_ERR_MSG(extack, "TC action name too long");
-                       err = -EINVAL;
-                       goto err_out;
+                       return ERR_PTR(-EINVAL);
                }
        }
 
@@ -996,24 +978,56 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
                 * indicate this using -EAGAIN.
                 */
                if (a_o != NULL) {
-                       err = -EAGAIN;
-                       goto err_mod;
+                       module_put(a_o->owner);
+                       return ERR_PTR(-EAGAIN);
                }
 #endif
                NL_SET_ERR_MSG(extack, "Failed to load TC action module");
-               err = -ENOENT;
-               goto err_free;
+               return ERR_PTR(-ENOENT);
        }
 
+       return a_o;
+}
+
+struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
+                                   struct nlattr *nla, struct nlattr *est,
+                                   char *name, int ovr, int bind,
+                                   struct tc_action_ops *a_o, bool rtnl_held,
+                                   struct netlink_ext_ack *extack)
+{
+       struct nla_bitfield32 flags = { 0, 0 };
+       u8 hw_stats = TCA_ACT_HW_STATS_ANY;
+       struct nlattr *tb[TCA_ACT_MAX + 1];
+       struct tc_cookie *cookie = NULL;
+       struct tc_action *a;
+       int err;
+
        /* backward compatibility for policer */
-       if (name == NULL)
+       if (name == NULL) {
+               err = nla_parse_nested_deprecated(tb, TCA_ACT_MAX, nla,
+                                                 tcf_action_policy, extack);
+               if (err < 0)
+                       return ERR_PTR(err);
+               if (tb[TCA_ACT_COOKIE]) {
+                       cookie = nla_memdup_cookie(tb);
+                       if (!cookie) {
+                               NL_SET_ERR_MSG(extack, "No memory to generate TC cookie");
+                               err = -ENOMEM;
+                               goto err_out;
+                       }
+               }
+               hw_stats = tcf_action_hw_stats_get(tb[TCA_ACT_HW_STATS]);
+               if (tb[TCA_ACT_FLAGS])
+                       flags = nla_get_bitfield32(tb[TCA_ACT_FLAGS]);
+
                err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, &a, ovr, bind,
                                rtnl_held, tp, flags.value, extack);
-       else
+       } else {
                err = a_o->init(net, nla, est, &a, ovr, bind, rtnl_held,
                                tp, flags.value, extack);
+       }
        if (err < 0)
-               goto err_mod;
+               goto err_out;
 
        if (!name && tb[TCA_ACT_COOKIE])
                tcf_set_action_cookie(&a->act_cookie, cookie);
@@ -1030,14 +1044,11 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
 
        return a;
 
-err_mod:
-       module_put(a_o->owner);
-err_free:
+err_out:
        if (cookie) {
                kfree(cookie->data);
                kfree(cookie);
        }
-err_out:
        return ERR_PTR(err);
 }
 
@@ -1048,6 +1059,7 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
                    struct tc_action *actions[], size_t *attr_size,
                    bool rtnl_held, struct netlink_ext_ack *extack)
 {
+       struct tc_action_ops *ops[TCA_ACT_MAX_PRIO] = {};
        struct nlattr *tb[TCA_ACT_MAX_PRIO + 1];
        struct tc_action *act;
        size_t sz = 0;
@@ -1059,9 +1071,20 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
        if (err < 0)
                return err;
 
+       for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) {
+               struct tc_action_ops *a_o;
+
+               a_o = tc_action_load_ops(name, tb[i], rtnl_held, extack);
+               if (IS_ERR(a_o)) {
+                       err = PTR_ERR(a_o);
+                       goto err_mod;
+               }
+               ops[i - 1] = a_o;
+       }
+
        for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) {
                act = tcf_action_init_1(net, tp, tb[i], est, name, ovr, bind,
-                                       rtnl_held, extack);
+                                       ops[i - 1], rtnl_held, extack);
                if (IS_ERR(act)) {
                        err = PTR_ERR(act);
                        goto err;
@@ -1081,6 +1104,11 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
 
 err:
        tcf_action_destroy(actions, bind);
+err_mod:
+       for (i = 0; i < TCA_ACT_MAX_PRIO; i++) {
+               if (ops[i])
+                       module_put(ops[i]->owner);
+       }
        return err;
 }