@@ -198,8 +198,10 @@ int tcf_idr_check_alloc(struct tc_action_net *tn, u32 *index,
int tcf_idr_release(struct tc_action *a, bool bind);
int tcf_register_action(struct tc_action_ops *a, struct pernet_operations *ops);
+int tcf_register_dyn_action(struct net *net, struct tc_action_ops *act);
int tcf_unregister_action(struct tc_action_ops *a,
struct pernet_operations *ops);
+int tcf_unregister_dyn_action(struct net *net, struct tc_action_ops *act);
int tcf_action_destroy(struct tc_action *actions[], int bind);
int tcf_action_exec(struct sk_buff *skb, struct tc_action **actions,
int nr_actions, struct tcf_result *res);
@@ -207,8 +209,8 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
struct nlattr *est,
struct tc_action *actions[], int init_res[], size_t *attr_size,
u32 flags, u32 fl_flags, struct netlink_ext_ack *extack);
-struct tc_action_ops *tc_action_load_ops(struct nlattr *nla, bool police,
- bool rtnl_held,
+struct tc_action_ops *tc_action_load_ops(struct net *net, struct nlattr *nla,
+ bool police, bool rtnl_held,
struct netlink_ext_ack *extack);
struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
struct nlattr *nla, struct nlattr *est,
@@ -57,6 +57,43 @@ static void tcf_free_cookie_rcu(struct rcu_head *p)
kfree(cookie);
}
+static unsigned int dyn_act_net_id;
+
+struct tcf_dyn_act_net {
+ struct idr act_base;
+ rwlock_t act_mod_lock;
+};
+
+static __net_init int tcf_dyn_act_base_init_net(struct net *net)
+{
+ struct tcf_dyn_act_net *dyn_base_net = net_generic(net, dyn_act_net_id);
+ rwlock_t _act_mod_lock = __RW_LOCK_UNLOCKED(_act_mod_lock);
+
+ idr_init(&dyn_base_net->act_base);
+ dyn_base_net->act_mod_lock = _act_mod_lock;
+ return 0;
+}
+
+static void __net_exit tcf_dyn_act_base_exit_net(struct net *net)
+{
+ struct tcf_dyn_act_net *dyn_base_net = net_generic(net, dyn_act_net_id);
+ struct tc_action_ops *ops;
+ unsigned long opid, tmp;
+
+ idr_for_each_entry_ul(&dyn_base_net->act_base, ops, tmp, opid) {
+ idr_remove(&dyn_base_net->act_base, ops->id);
+ }
+
+ idr_destroy(&dyn_base_net->act_base);
+}
+
+static struct pernet_operations tcf_dyn_act_base_net_ops = {
+ .init = tcf_dyn_act_base_init_net,
+ .exit = tcf_dyn_act_base_exit_net,
+ .id = &dyn_act_net_id,
+ .size = sizeof(struct tc_action_ops),
+};
+
static void tcf_set_action_cookie(struct tc_cookie __rcu **old_cookie,
struct tc_cookie *new_cookie)
{
@@ -941,6 +978,29 @@ static void tcf_pernet_del_id_list(unsigned int id)
mutex_unlock(&act_id_mutex);
}
+int tcf_register_dyn_action(struct net *net, struct tc_action_ops *act)
+{
+ struct tcf_dyn_act_net *dyn_base_net = net_generic(net, dyn_act_net_id);
+ int ret;
+
+ write_lock(&dyn_base_net->act_mod_lock);
+
+ /* Dynamic actions start counting after TCA_ID_MAX + 1*/
+ act->id = TCA_ID_MAX + 1;
+
+ ret = idr_alloc_u32(&dyn_base_net->act_base, act, &act->id,
+ USHRT_MAX, GFP_ATOMIC);
+ if (ret < 0)
+ goto err_out;
+ write_unlock(&dyn_base_net->act_mod_lock);
+
+ return 0;
+
+err_out:
+ write_unlock(&dyn_base_net->act_mod_lock);
+ return ret;
+}
+
int tcf_register_action(struct tc_action_ops *act,
struct pernet_operations *ops)
{
@@ -1010,40 +1070,84 @@ int tcf_unregister_action(struct tc_action_ops *act,
}
EXPORT_SYMBOL(tcf_unregister_action);
+int tcf_unregister_dyn_action(struct net *net, struct tc_action_ops *act)
+{
+ struct tcf_dyn_act_net *dyn_base_net = net_generic(net, dyn_act_net_id);
+ int err = 0;
+
+ write_lock(&dyn_base_net->act_mod_lock);
+ if (!idr_remove(&dyn_base_net->act_base, act->id))
+ err = -EINVAL;
+
+ write_unlock(&dyn_base_net->act_mod_lock);
+
+ return err;
+}
+EXPORT_SYMBOL(tcf_unregister_dyn_action);
+
/* lookup by name */
-static struct tc_action_ops *tc_lookup_action_n(char *kind)
+static struct tc_action_ops *tc_lookup_action_n(struct net *net, char *kind)
{
+ struct tcf_dyn_act_net *dyn_base_net = net_generic(net, dyn_act_net_id);
struct tc_action_ops *a, *res = NULL;
if (kind) {
+ unsigned long tmp, id;
+
read_lock(&act_mod_lock);
list_for_each_entry(a, &act_base, head) {
+ if (strcmp(kind, a->kind) == 0) {
+ if (try_module_get(a->owner)) {
+ read_unlock(&act_mod_lock);
+ return a;
+ }
+ }
+ }
+ read_unlock(&act_mod_lock);
+
+ read_lock(&dyn_base_net->act_mod_lock);
+ idr_for_each_entry_ul(&dyn_base_net->act_base, a, tmp, id) {
if (strcmp(kind, a->kind) == 0) {
if (try_module_get(a->owner))
res = a;
break;
}
}
- read_unlock(&act_mod_lock);
+ read_unlock(&dyn_base_net->act_mod_lock);
}
return res;
}
/* lookup by nlattr */
-static struct tc_action_ops *tc_lookup_action(struct nlattr *kind)
+static struct tc_action_ops *tc_lookup_action(struct net *net,
+ struct nlattr *kind)
{
+ struct tcf_dyn_act_net *dyn_base_net = net_generic(net, dyn_act_net_id);
struct tc_action_ops *a, *res = NULL;
if (kind) {
+ unsigned long tmp, id;
+
read_lock(&act_mod_lock);
list_for_each_entry(a, &act_base, head) {
+ if (nla_strcmp(kind, a->kind) == 0) {
+ if (try_module_get(a->owner)) {
+ read_unlock(&act_mod_lock);
+ return a;
+ }
+ }
+ }
+ read_unlock(&act_mod_lock);
+
+ read_lock(&dyn_base_net->act_mod_lock);
+ idr_for_each_entry_ul(&dyn_base_net->act_base, a, tmp, id) {
if (nla_strcmp(kind, a->kind) == 0) {
if (try_module_get(a->owner))
res = a;
break;
}
}
- read_unlock(&act_mod_lock);
+ read_unlock(&dyn_base_net->act_mod_lock);
}
return res;
}
@@ -1294,8 +1398,8 @@ void tcf_idr_insert_many(struct tc_action *actions[])
}
}
-struct tc_action_ops *tc_action_load_ops(struct nlattr *nla, bool police,
- bool rtnl_held,
+struct tc_action_ops *tc_action_load_ops(struct net *net, struct nlattr *nla,
+ bool police, bool rtnl_held,
struct netlink_ext_ack *extack)
{
struct nlattr *tb[TCA_ACT_MAX + 1];
@@ -1326,7 +1430,7 @@ struct tc_action_ops *tc_action_load_ops(struct nlattr *nla, bool police,
}
}
- a_o = tc_lookup_action_n(act_name);
+ a_o = tc_lookup_action_n(net, act_name);
if (a_o == NULL) {
#ifdef CONFIG_MODULES
if (rtnl_held)
@@ -1335,7 +1439,7 @@ struct tc_action_ops *tc_action_load_ops(struct nlattr *nla, bool police,
if (rtnl_held)
rtnl_lock();
- a_o = tc_lookup_action_n(act_name);
+ a_o = tc_lookup_action_n(net, act_name);
/* We dropped the RTNL semaphore in order to
* perform the module load. So, even if we
@@ -1445,7 +1549,8 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) {
struct tc_action_ops *a_o;
- a_o = tc_action_load_ops(tb[i], flags & TCA_ACT_FLAGS_POLICE,
+ a_o = tc_action_load_ops(net, tb[i],
+ flags & TCA_ACT_FLAGS_POLICE,
!(flags & TCA_ACT_FLAGS_NO_RTNL),
extack);
if (IS_ERR(a_o)) {
@@ -1655,7 +1760,7 @@ static struct tc_action *tcf_action_get_1(struct net *net, struct nlattr *nla,
index = nla_get_u32(tb[TCA_ACT_INDEX]);
err = -EINVAL;
- ops = tc_lookup_action(tb[TCA_ACT_KIND]);
+ ops = tc_lookup_action(net, tb[TCA_ACT_KIND]);
if (!ops) { /* could happen in batch of actions */
NL_SET_ERR_MSG(extack, "Specified TC action kind not found");
goto err_out;
@@ -1703,7 +1808,7 @@ static int tca_action_flush(struct net *net, struct nlattr *nla,
err = -EINVAL;
kind = tb[TCA_ACT_KIND];
- ops = tc_lookup_action(kind);
+ ops = tc_lookup_action(net, kind);
if (!ops) { /*some idjot trying to flush unknown action */
NL_SET_ERR_MSG(extack, "Cannot flush unknown TC action");
goto err_out;
@@ -2109,7 +2214,7 @@ static int tc_dump_action(struct sk_buff *skb, struct netlink_callback *cb)
return 0;
}
- a_o = tc_lookup_action(kind);
+ a_o = tc_lookup_action(net, kind);
if (a_o == NULL)
return 0;
@@ -2176,6 +2281,7 @@ static int __init tc_action_init(void)
rtnl_register(PF_UNSPEC, RTM_GETACTION, tc_ctl_action, tc_dump_action,
0);
+ register_pernet_subsys(&tcf_dyn_act_base_net_ops);
return 0;
}
@@ -3271,7 +3271,7 @@ int tcf_exts_validate_ex(struct net *net, struct tcf_proto *tp, struct nlattr **
if (exts->police && tb[exts->police]) {
struct tc_action_ops *a_o;
- a_o = tc_action_load_ops(tb[exts->police], true,
+ a_o = tc_action_load_ops(net, tb[exts->police], true,
!(flags & TCA_ACT_FLAGS_NO_RTNL),
extack);
if (IS_ERR(a_o))