Message ID | 20230930143542.101000-6-jhs@mojatatu.com (mailing list archive) |
---|---|
State | RFC |
Delegated to: | Netdev Maintainers |
Headers | show |
Series | Introducing P4TC | expand |
On Sat 30 Sep 2023 at 10:35, Jamal Hadi Salim <jhs@mojatatu.com> wrote: > In P4, actions are assumed to pre exist and have an upper bound number of > instances. Typically if you have 1M table entries you want to allocate > enough action instances to cover the 1M entries. However, this is a big > waste of memory if the action instances are not in use. So for our case, > we allow the user to specify a minimal amount of actions in the template > and then if more dynamic action instances are needed then they will be > added on demand as in the current approach with tc filter-action > relationship. > > Add the necessary code to preallocate actions instances for dynamic > actions. > > We add 2 new actions flags: > - TCA_ACT_FLAGS_PREALLOC: Indicates the action instance is a dynamic action > and was preallocated for future use the templating phase of P4TC > - TCA_ACT_FLAGS_UNREFERENCED: Indicates the action instance was > preallocated and is currently not being referenced by any other object. > Which means it won't show up in an action instance dump. > > Once an action instance is created we don't free it when the last table > entry referring to it is deleted. > Instead we add it to the pool/cache of action instances for > that specific action i.e it counts as if it is preallocated. > Preallocated actions can't be deleted by the tc actions runtime commands > and a dump or a get will only show preallocated actions > instances which are being used (TCA_ACT_FLAGS_UNREFERENCED == false). > > The preallocated actions will be deleted once the pipeline is deleted > (which will purge the dynamic action kind and its instances). > > For example, if we were to create a dynamic action that preallocates 128 > elements and dumped: > > $ tc -j p4template get action/myprog/send_nh | jq . > > We'd see the following: > > [ > { > "obj": "action template", > "pname": "myprog", > "pipeid": 1 > }, > { > "templates": [ > { > "aname": "myprog/send_nh", > "actid": 1, > "params": [ > { > "name": "port", > "type": "dev", > "id": 1 > } > ], > "prealloc": 128 > } > ] > } > ] > > If we try to dump the dynamic action instances, we won't see any: > > $ tc -j actions ls action myprog/send_nh | jq . > > [] > > However, if we create a table entry which references this action kind: > > $ tc p4ctrl create myprog/table/cb/FDB \ > dstAddr d2:96:91:5d:02:86 action myprog/send_nh \ > param port type dev dummy0 > > Dumping the action instance will now show this one instance which is > associated with the table entry: > > $ tc -j actions ls action myprog/send_nh | jq . > > [ > { > "total acts": 1 > }, > { > "actions": [ > { > "order": 0, > "kind": "myprog/send_nh", > "index": 1, > "ref": 1, > "bind": 1, > "params": [ > { > "name": "port", > "type": "dev", > "value": "dummy0", > "id": 1 > } > ], > "not_in_hw": true > } > ] > } > ] > > Co-developed-by: Victor Nogueira <victor@mojatatu.com> > Signed-off-by: Victor Nogueira <victor@mojatatu.com> > Co-developed-by: Pedro Tammela <pctammela@mojatatu.com> > Signed-off-by: Pedro Tammela <pctammela@mojatatu.com> > Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com> > --- act_api part looks good to me. [...]
diff --git a/include/net/act_api.h b/include/net/act_api.h index 90e215f10..cd5a8e86f 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -68,6 +68,8 @@ struct tc_action { #define TCA_ACT_FLAGS_REPLACE (1U << (TCA_ACT_FLAGS_USER_BITS + 2)) #define TCA_ACT_FLAGS_NO_RTNL (1U << (TCA_ACT_FLAGS_USER_BITS + 3)) #define TCA_ACT_FLAGS_AT_INGRESS (1U << (TCA_ACT_FLAGS_USER_BITS + 4)) +#define TCA_ACT_FLAGS_PREALLOC (1U << (TCA_ACT_FLAGS_USER_BITS + 5)) +#define TCA_ACT_FLAGS_UNREFERENCED (1U << (TCA_ACT_FLAGS_USER_BITS + 6)) /* Update lastuse only if needed, to avoid dirtying a cache line. * We use a temp variable to avoid fetching jiffies twice. @@ -200,6 +202,7 @@ int tcf_idr_create_from_flags(struct tc_action_net *tn, u32 index, const struct tc_action_ops *ops, int bind, u32 flags); void tcf_idr_insert_many(struct tc_action *actions[]); +void tcf_idr_insert_n(struct tc_action *actions[], const u32 n); void tcf_idr_cleanup(struct tc_action_net *tn, u32 index); int tcf_idr_check_alloc(struct tc_action_net *tn, u32 *index, struct tc_action **a, int bind); diff --git a/net/sched/act_api.c b/net/sched/act_api.c index cfa74f521..6f4c29e90 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -560,6 +560,8 @@ static int tcf_dump_walker(struct tcf_idrinfo *idrinfo, struct sk_buff *skb, continue; if (IS_ERR(p)) continue; + if (p->tcfa_flags & TCA_ACT_FLAGS_UNREFERENCED) + continue; if (jiffy_since && time_after(jiffy_since, @@ -640,6 +642,9 @@ static int tcf_del_walker(struct tcf_idrinfo *idrinfo, struct sk_buff *skb, idr_for_each_entry_ul(idr, p, tmp, id) { if (IS_ERR(p)) continue; + if (p->tcfa_flags & TCA_ACT_FLAGS_PREALLOC) + continue; + ret = tcf_idr_release_unsafe(p); if (ret == ACT_P_DELETED) module_put(ops->owner); @@ -1367,26 +1372,38 @@ static const struct nla_policy tcf_action_policy[TCA_ACT_MAX + 1] = { [TCA_ACT_HW_STATS] = NLA_POLICY_BITFIELD32(TCA_ACT_HW_STATS_ANY), }; +static void tcf_idr_insert_1(struct tc_action *a) +{ + struct tcf_idrinfo *idrinfo; + + idrinfo = a->idrinfo; + mutex_lock(&idrinfo->lock); + /* Replace ERR_PTR(-EBUSY) allocated by tcf_idr_check_alloc if + * it is just created, otherwise this is just a nop. + */ + idr_replace(&idrinfo->action_idr, a, a->tcfa_index); + mutex_unlock(&idrinfo->lock); +} + void tcf_idr_insert_many(struct tc_action *actions[]) { int i; for (i = 0; i < TCA_ACT_MAX_PRIO; i++) { - struct tc_action *a = actions[i]; - struct tcf_idrinfo *idrinfo; - - if (!a) + if (!actions[i]) continue; - idrinfo = a->idrinfo; - mutex_lock(&idrinfo->lock); - /* Replace ERR_PTR(-EBUSY) allocated by tcf_idr_check_alloc if - * it is just created, otherwise this is just a nop. - */ - idr_replace(&idrinfo->action_idr, a, a->tcfa_index); - mutex_unlock(&idrinfo->lock); + tcf_idr_insert_1(actions[i]); } } +void tcf_idr_insert_n(struct tc_action *actions[], const u32 n) +{ + int i; + + for (i = 0; i < n; i++) + tcf_idr_insert_1(actions[i]); +} + struct tc_action_ops *tc_action_load_ops(struct net *net, struct nlattr *nla, bool police, bool rtnl_held, struct netlink_ext_ack *extack) @@ -2033,8 +2050,17 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n, ret = PTR_ERR(act); goto err; } - attr_size += tcf_action_fill_size(act); actions[i - 1] = act; + + if (event == RTM_DELACTION && + act->tcfa_flags & TCA_ACT_FLAGS_PREALLOC) { + ret = -EINVAL; + NL_SET_ERR_MSG_FMT(extack, + "Unable to delete preallocated action %s", + act->ops->kind); + goto err; + } + attr_size += tcf_action_fill_size(act); } attr_size = tcf_action_full_attrs_size(attr_size);