Skip to content

Commit

Permalink
p4tc: Add P4 extern interface
Browse files Browse the repository at this point in the history
P4 externs are an abstraction in the language to call for extending
language functionality. For example, the function that sends a packet to a
specific port (send_to_port) in P4 PNA is an extern.

Externs can be seen as classes, which have constructors and methods.
Take, for example, the Register extern definition:

extern Register<T> {
    Register(@tc_numel bit<32> size);
    @tc_md_read T read(@tc_key bit<32> index);
    @tc_md_write void write(@tc_key bit<32> index, @tc_data T value);
}

struct tc_ControlPath_Register<T> {
    @tc_key bit<32> index;
    @tc_data T value;
}

Which can then be instantiated within a P4 program as:
Register<bit<32>>(128) reg1;
Register<bit<16>>(1024) reg2;

Will be abstracted into the template by the P4C compiler for "reg1" as
follows:

tc p4template create extern/root/register extid 10 numinstances 2
tc p4template create extern_inst/aP4Proggie/register/reg1 instid 1 \
control_path tc_key index type bit32 tc_data value type bit32 \
numelemens 128 default_value 22

=========================EXTERN RUNTIME COMMANDS=========================

Once we seal the pipeline, the register values will be assigned to the
default value specified on the template as "default_value". After sealing,
we can update the runtime instance element. For example to update
reg1[2] with the value 33, we will do the following:

tc p4ctrl update aP4proggie/extern/register/reg1 tc_key index 2 \
tc_data value 33

We can also get its value:

tc p4ctrl get aP4proggie/extern/register/reg1 tc_key index 2

Which will yield the following output:

total exts 0
        extern order 1:
          tc_key index id 1 type bit32 value: 1
          tc_data value id 2 type bit32 value: 33

We can also dump all of the elements in this register:

tc p4ctrl get aP4proggie/extern/register/reg1

Note that the only valid runtime operations are get and update.

=========================EXTERN P4 Runtime =========================

The generated ebpf code invokes the externs in the P4TC domain
using the md_read or md_write kfuncs, for example:
if the P4 program had this invocation:

tmp1 = reg1.read(index1);

Then equivalent generated ebpf code is as follows:

param.pipe_id = aP4Proggie_ID;
param.ext_id = EXTERN_REGISTER;
param.inst_id = EXTERN_REGISTER_INSTANCE_ID1;
param.index = index1;
param.param_id = EXTERN_REGISTER_PARAM_ID;
bpf_p4tc_extern_md_read(skb, &res, &param);
tmp1 = (u32 *)res.params;

Co-developed-by: Victor Nogueira <[email protected]>
Signed-off-by: Victor Nogueira <[email protected]>
Co-developed-by: Pedro Tammela <[email protected]>
Signed-off-by: Pedro Tammela <[email protected]>
Signed-off-by: Jamal Hadi Salim <[email protected]>
  • Loading branch information
jhsmt authored and vbnogueira committed Nov 16, 2023
1 parent 4c33e5c commit 5a4f811
Show file tree
Hide file tree
Showing 13 changed files with 5,083 additions and 10 deletions.
161 changes: 161 additions & 0 deletions include/net/p4tc.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <linux/rhashtable-types.h>
#include <net/tc_act/p4tc.h>
#include <net/p4tc_types.h>
#include <linux/bpf.h>

#define P4TC_DEFAULT_NUM_TABLES P4TC_MINTABLES_COUNT
#define P4TC_DEFAULT_MAX_RULES 1
Expand All @@ -21,6 +22,10 @@
#define P4TC_DEFAULT_TMASKS 8
#define P4TC_MAX_T_AGING 864000000
#define P4TC_DEFAULT_T_AGING 30000
#define P4TC_DEFAULT_NUM_EXT_INSTS 1
#define P4TC_MAX_NUM_EXT_INSTS 1024
#define P4TC_DEFAULT_NUM_EXT_INST_ELEMS 1
#define P4TC_MAX_NUM_EXT_INST_ELEMS 1024

#define P4TC_MAX_PERMISSION (GENMASK(P4TC_PERM_MAX_BIT, 0))

Expand All @@ -30,6 +35,8 @@
#define P4TC_TBLID_IDX 1
#define P4TC_AID_IDX 1
#define P4TC_PARSEID_IDX 1
#define P4TC_TMPL_EXT_IDX 1
#define P4TC_TMPL_EXT_INST_IDX 2

struct p4tc_dump_ctx {
u32 ids[P4TC_PATH_MAX];
Expand Down Expand Up @@ -79,9 +86,14 @@ struct p4tc_pipeline {
struct p4tc_template_common common;
struct idr p_act_idr;
struct idr p_tbl_idr;
/* IDR where the externs are stored globally in the root pipeline */
struct idr p_ext_idr;
/* IDR where the per user pipeline data related to externs is stored */
struct idr user_ext_idr;
struct rcu_head rcu;
struct net *net;
u32 num_created_acts;
u32 num_created_ext_elems;
/* Accounts for how many entities are referencing this pipeline.
* As for now only P4 filters can refer to pipelines.
*/
Expand Down Expand Up @@ -207,6 +219,27 @@ static inline int p4tc_action_destroy(struct tc_action **acts)

#define P4TC_MAX_PARAM_DATA_SIZE 124

#define P4TC_EXT_FLAGS_UNSPEC 0x0
#define P4TC_EXT_FLAGS_CONTROL_READ 0x1
#define P4TC_EXT_FLAGS_CONTROL_WRITE 0x2

struct p4tc_ext_bpf_params {
u32 pipe_id;
u32 ext_id;
u32 inst_id;
u32 index;
u32 param_id;
u32 flags;
u8 in_params[128]; /* extern specific params if any */
};

struct p4tc_ext_bpf_res {
u32 ext_id;
u32 index;
u32 verdict;
u8 out_params[128]; /* specific values if any */
};

struct p4tc_table_defact {
struct tc_action **default_acts;
/* Will have two 7 bits blocks containing CRUDXPS (Create, read, update,
Expand Down Expand Up @@ -237,6 +270,7 @@ struct p4tc_table {
struct p4tc_table_perm __rcu *tbl_permissions;
struct p4tc_table_entry_mask __rcu **tbl_masks_array;
unsigned long __rcu *tbl_free_masks_bitmap;
struct p4tc_extern_inst *tbl_counter;
u64 tbl_aging;
/* Locks the available masks IDR which will be used when adding and
* deleting table entries.
Expand Down Expand Up @@ -352,6 +386,7 @@ struct p4tc_table_entry_key {
struct p4tc_table_entry_value {
u32 prio;
int num_acts;
struct p4tc_extern_common *counter;
struct tc_action **acts;
/* Accounts for how many entities are referencing, eg: Data path,
* one or more control path and timer.
Expand Down Expand Up @@ -576,8 +611,134 @@ static inline bool p4tc_runtime_msg_is_update(struct nlmsghdr *n)
return n->nlmsg_type == RTM_P4TC_UPDATE;
}

struct p4tc_user_pipeline_extern {
struct idr e_inst_idr;
struct p4tc_tmpl_extern *tmpl_ext;
void (*free)(struct p4tc_user_pipeline_extern *pipe_ext,
struct idr *tmpl_exts_idr);
u32 ext_id;
/* Accounts for how many instances refer to this extern. */
refcount_t ext_ref;
/* Counts how many instances this extern has */
atomic_t curr_insts_num;
u32 PAD0;
char ext_name[EXTERNNAMSIZ];
};

struct p4tc_tmpl_extern {
struct p4tc_template_common common;
struct idr params_idr;
const struct p4tc_extern_ops *ops;
u32 ext_id;
u32 num_params;
u32 max_num_insts;
/* Accounts for how many pipelines refer to this extern. */
refcount_t tmpl_ref;
char mod_name[MODULE_NAME_LEN];
bool has_exec_method;
};

struct p4tc_extern_inst {
struct p4tc_template_common common;
struct p4tc_extern_params *params;
const struct p4tc_extern_ops *ops;
struct idr control_elems_idr;
struct list_head unused_elems;
/* Locks the available externs list.
* Which will be used by table entries that reference externs (refer to
* direct counters and meters in P4).
* Note that table entries can be created, update or deleted by both
* control and data path. So this list may be modified from both
* contexts.
*/
spinlock_t available_list_lock;
/* Accounts for how many elements refer to this extern. */
refcount_t inst_ref;
struct p4tc_user_pipeline_extern *pipe_ext;
char *ext_name;
/* Counts how many elements this extern has */
atomic_t curr_num_elems;
u32 max_num_elems;
u32 ext_id;
u32 ext_inst_id;
u32 num_control_params;
struct p4tc_extern_params *constr_params;
u32 flags;
bool tbl_bindable;
bool is_scalar;
};

int p4tc_pipeline_create_extern_net(struct p4tc_tmpl_extern *tmpl_ext);
int p4tc_pipeline_del_extern_net(struct p4tc_tmpl_extern *tmpl_ext);
struct p4tc_extern_inst *
p4tc_ext_inst_find_bynames(struct net *net, struct p4tc_pipeline *pipeline,
const char *extname, const char *instname,
struct netlink_ext_ack *extack);
struct p4tc_user_pipeline_extern *
p4tc_pipe_ext_find_bynames(struct net *net, struct p4tc_pipeline *pipeline,
const char *extname, struct netlink_ext_ack *extack);
struct p4tc_extern_inst *
p4tc_ext_inst_table_bind(struct p4tc_pipeline *pipeline,
struct p4tc_user_pipeline_extern **pipe_ext,
const char *ext_inst_path,
struct netlink_ext_ack *extack);
void
p4tc_ext_inst_table_unbind(struct p4tc_table *table,
struct p4tc_user_pipeline_extern *pipe_ext,
struct p4tc_extern_inst *inst);
struct p4tc_extern_inst *
p4tc_ext_inst_get_byids(struct net *net, struct p4tc_pipeline **pipeline,
struct p4tc_ext_bpf_params *params);
struct p4tc_extern_inst *
p4tc_ext_find_byids(struct p4tc_pipeline *pipeline,
const u32 ext_id, const u32 inst_id);
struct p4tc_extern_inst *
p4tc_ext_inst_alloc(const struct p4tc_extern_ops *ops, const u32 max_num_elems,
bool tbl_bindable, char *ext_name);

int __bpf_p4tc_extern_md_write(struct net *net,
struct p4tc_ext_bpf_params *params);
int __bpf_p4tc_extern_md_read(struct net *net,
struct p4tc_ext_bpf_res *res,
struct p4tc_ext_bpf_params *params);
struct p4tc_extern_params *
p4tc_ext_params_copy(struct p4tc_extern_params *params_orig);

extern const struct p4tc_template_ops p4tc_tmpl_ext_ops;
extern const struct p4tc_template_ops p4tc_ext_inst_ops;

struct p4tc_extern_param {
struct p4tc_extern_param_ops *ops;
struct p4tc_extern_param_ops *mod_ops;
void *value;
struct p4tc_type *type;
struct p4tc_type_mask_shift *mask_shift;
u32 id;
u32 index;
u32 bitsz;
u32 flags;
char name[EXTPARAMNAMSIZ];
struct rcu_head rcu;
};

struct p4tc_extern_param_ops {
int (*init_value)(struct net *net,
struct p4tc_extern_param *nparam, void *value,
struct netlink_ext_ack *extack);
/* Only for bit<X> parameter types */
void (*default_value)(struct p4tc_extern_param *nparam);
int (*dump_value)(struct sk_buff *skb, struct p4tc_extern_param_ops *op,
struct p4tc_extern_param *param);
void (*free)(struct p4tc_extern_param *param);
u32 len;
u32 alloc_len;
};

#define to_pipeline(t) ((struct p4tc_pipeline *)t)
#define to_act(t) ((struct p4tc_act *)t)
#define to_table(t) ((struct p4tc_table *)t)

#define to_extern(t) ((struct p4tc_tmpl_extern *)t)
#define to_extern_inst(t) ((struct p4tc_extern_inst *)t)

#endif
Loading

0 comments on commit 5a4f811

Please sign in to comment.