@@ -105,6 +105,7 @@ typedef void (*tc_action_priv_destructor)(void *priv);
struct tc_action_ops {
struct list_head head;
+ struct list_head dyn_head;
char kind[IFNAMSIZ];
enum tca_id id; /* identifier should match kind */
unsigned int net_id;
@@ -198,8 +199,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);
+void 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 +210,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,40 @@ 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 list_head 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);
+
+ INIT_LIST_HEAD(&dyn_base_net->act_base);
+ rwlock_init(&dyn_base_net->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, *tmp;
+
+ list_for_each_entry_safe(ops, tmp, &dyn_base_net->act_base, dyn_head) {
+ list_del(&ops->dyn_head);
+ }
+}
+
+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 +975,48 @@ static void tcf_pernet_del_id_list(unsigned int id)
mutex_unlock(&act_id_mutex);
}
+static struct tc_action_ops *tc_lookup_dyn_action(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;
+
+ read_lock(&dyn_base_net->act_mod_lock);
+ list_for_each_entry(a, &dyn_base_net->act_base, dyn_head) {
+ if (strcmp(kind, a->kind) == 0) {
+ if (try_module_get(a->owner))
+ res = a;
+ break;
+ }
+ }
+ read_unlock(&dyn_base_net->act_mod_lock);
+
+ return res;
+}
+
+void 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);
+
+ write_lock(&dyn_base_net->act_mod_lock);
+ list_del(&act->dyn_head);
+ write_unlock(&dyn_base_net->act_mod_lock);
+}
+EXPORT_SYMBOL(tcf_unregister_dyn_action);
+
+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);
+
+ if (tc_lookup_dyn_action(net, act->kind))
+ return -EEXIST;
+
+ write_lock(&dyn_base_net->act_mod_lock);
+ list_add(&act->dyn_head, &dyn_base_net->act_base);
+ write_unlock(&dyn_base_net->act_mod_lock);
+
+ return 0;
+}
+
int tcf_register_action(struct tc_action_ops *act,
struct pernet_operations *ops)
{
@@ -1011,7 +1087,7 @@ int tcf_unregister_action(struct tc_action_ops *act,
EXPORT_SYMBOL(tcf_unregister_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 tc_action_ops *a, *res = NULL;
@@ -1019,31 +1095,48 @@ static struct tc_action_ops *tc_lookup_action_n(char *kind)
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))
- res = a;
- break;
+ if (try_module_get(a->owner)) {
+ read_unlock(&act_mod_lock);
+ return a;
+ }
}
}
read_unlock(&act_mod_lock);
+
+ return tc_lookup_dyn_action(net, kind);
}
+
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) {
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);
+ list_for_each_entry(a, &dyn_base_net->act_base, dyn_head) {
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 +1387,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 +1419,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 +1428,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 +1538,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 +1749,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 +1797,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 +2203,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 +2270,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;
}
@@ -3293,7 +3293,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))