On 17-02-16 07:22 AM, Jiri Pirko wrote: > From: Arkadi Sharshevsky <arka...@mellanox.com> > > The pipeline debug is used to export the pipeline abstractions > for the main objects - tables, headers and entries. The only support for > set is for changing the counter parameter on specific table. >
Couple quick comments this morning I'll review the code later today. > The basic structures: > > Header - can represent a real protocol header information or internal > metadata. Generic protocol headers like IPv4 can be shared > between drivers. Each driver can add local headers. > > Field - part of a header. Can represent protocol field or specific > ASIC metadata field. Hardware special metadata fields can > be mapped to different resources, for example switch ASIC > ports can have internal number which from the systems > point of view is mapped to netdeivce ifindex. > > Hfield - Specific combination of header:field. This object is used > to represent the table behavior in terms of match/action. > > Hfield_val - Represents value of specific Hfield. > > Table - represents a hardware block which can be described with > match/action behavior. The match/action can be done on the > packets data or on the internal metadata that it gathered > along the packets traversal throw the pipeline which is vendor > specific and should be exported in order to provide > understanding of ASICs behavior. > > Entry - represents single record in a specific table. The entry is > identified by specific combination of Hfield_vals for match > /action. Should actions have their own type? I think this helps user space and in general keep things clean. > > Prior to accessing the tables/entries the drivers provide the header/ > field data base which is used by driver to user-space. The data base > is split between the shared headers and unique headers. > Having a header definition file in uapi that has known field:header values is a nice optimization for user space that deals with multiple devices. This way I can avoid string parsing and just look for my ipv6 header if I am writing a simple user space application. More advanced (and I'm guessing the stuff being used on the mlx switch or at least a lot of the applications I use) probably don't need this but it is helpful on the lower end less specialized equipment. Thanks for doing this quick scan looks nice. John > Signed-off-by: Arkadi Sharshevsky <arka...@mellanox.com> > Signed-off-by: Jiri Pirko <j...@mellanox.com> > --- > include/net/devlink.h | 224 ++++++++++++- > include/uapi/linux/devlink.h | 50 ++- > net/core/devlink.c | 747 > +++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 1019 insertions(+), 2 deletions(-) > > diff --git a/include/net/devlink.h b/include/net/devlink.h > index d29e5fc..d480669 100644 > --- a/include/net/devlink.h > +++ b/include/net/devlink.h > @@ -25,6 +25,8 @@ struct devlink { > struct list_head list; > struct list_head port_list; > struct list_head sb_list; > + struct list_head dpipe_table_list; > + struct devlink_dpipe_headers *dpipe_headers; > const struct devlink_ops *ops; > struct device *dev; > possible_net_t _net; > @@ -49,6 +51,157 @@ struct devlink_sb_pool_info { > enum devlink_sb_threshold_type threshold_type; > }; > > +/** > + * struct devlink_dpipe_field - dpipe field object > + * @name: field name > + * @id: id inside header fields array > + * @bitwidth: bitwidth > + * @mapping_type: mapping type > + */ > +struct devlink_dpipe_field { > + const char *name; > + unsigned int id; > + unsigned int bitwidth; > + enum devlink_dpipe_field_mapping_type mapping_type; > +}; > + > +/** > + * struct devlink_dpipe_header - dpipe header object > + * @name: header name > + * @id: id, global/local detrmined by global bit > + * @fields: fields > + * @fields_count: number of fields > + * @global: indicates of header is shared like most protocol header > + * or driver specific > + */ > +struct devlink_dpipe_header { > + const char *name; > + unsigned int id; > + struct devlink_dpipe_field *fields; > + unsigned int fields_count; > + bool global; > +}; > + > +/** > + * struct devlink_dpipe_hfield - represents header:field tuple > + * @header: header > + * @filed_id: field index > + */ > +struct devlink_dpipe_hfield { > + struct devlink_dpipe_header *header; > + unsigned int field_id; > +}; > + > +/** > + * struct devlink_dpipe_hfield_val - represents a value of specific > + * tuple > + * @hfield: the tuple the value is relevant for > + * @mapping_value: in case the field has some mapping this value > + * specified the mapping value > + * @value_size: value size > + * @value: the value > + * @mask: bit mask > + */ > +struct devlink_dpipe_hfield_val { > + struct devlink_dpipe_hfield *hfield; > + unsigned int mapping_value; > + unsigned int value_size; > + void *value; > + void *mask; > +}; > + > +/** > + * struct devlink_dpipe_entry - table entry object > + * @index: index of the entry in the table > + * @matches: tuple match values > + * @matches_count: count of matches tuples > + * @actions: tuple actions values > + * @actions_count: count of actions values > + * @counter: value of counter > + * @counter_valid: Specify if value is valid from hardware > + */ > +struct devlink_dpipe_entry { > + unsigned int index; > + struct devlink_dpipe_hfield_val *matches; > + unsigned int matches_count; > + struct devlink_dpipe_hfield_val *actions; > + unsigned int actions_count; > + u64 counter; > + bool counter_valid; > +}; > + > +/** > + * struct devlink_dpipe_dump_ctx - context provided to driver in order > + * to dump > + * @info: info > + * @cmd: devlink command > + * @skb: skb > + * @nest: top attribute > + * @hdr: hdr > + */ > +struct devlink_dpipe_dump_ctx { > + struct genl_info *info; > + enum devlink_command cmd; > + struct sk_buff *skb; > + struct nlattr *nest; > + void *hdr; > +}; > + > +struct devlink_dpipe_table_ops; > + > +/** > + * struct devlink_dpipe_table - table object > + * @priv: private > + * @name: table name > + * @size: maximum number of entries > + * @matches: header:field match tuples > + * @matches_count: count of match tuples > + * @actions: header:field action tuples > + * @actions_count: count of action tuples > + * @counters_enabled: indicates if counters are active > + * @counter_control_extern: indicates if counter control is in dpipe or > + * external tool > + * @table_ops: table operations > + * @rcu: rcu > + */ > +struct devlink_dpipe_table { > + void *priv; > + struct list_head list; > + const char *name; > + int size; > + struct devlink_dpipe_hfield *matches; > + unsigned int matches_count; > + struct devlink_dpipe_hfield *actions; > + unsigned int actions_count; > + bool counters_enabled; > + bool counter_control_extern; > + struct devlink_dpipe_table_ops *table_ops; > + struct rcu_head rcu; > +}; > + > +/** > + * struct devlink_dpipe_table_ops - dpipe_table ops > + * @entries_dump - dumps all active entries in the table > + * @counter_set_update - when changing the counter status hardware sync > + * maybe needed to allocate/free counter related > + * resources > + */ > +struct devlink_dpipe_table_ops { > + int (*entries_dump)(void *priv, bool counters_enabled, > + struct devlink_dpipe_dump_ctx *dump_ctx); > + int (*counter_set_update)(void *priv, bool enable); > +}; > + > +/** > + * struct devlink_dpipe_headers - dpipe headers > + * @headers - header array can be shared(global bit) or local > + * @headers_count - count of headers > + */ > +struct devlink_dpipe_headers { > + struct devlink_dpipe_header **headers; > + unsigned int headers_count; > +}; > + > struct devlink_ops { > int (*port_type_set)(struct devlink_port *devlink_port, > enum devlink_port_type port_type); > @@ -132,7 +285,26 @@ int devlink_sb_register(struct devlink *devlink, > unsigned int sb_index, > u16 egress_pools_count, u16 ingress_tc_count, > u16 egress_tc_count); > void devlink_sb_unregister(struct devlink *devlink, unsigned int sb_index); > - > +int devlink_dpipe_table_register(struct devlink *devlink, > + const char *table_name, > + struct devlink_dpipe_table_ops *table_ops, > + void *priv, > + struct devlink_dpipe_hfield *matches, > + unsigned int matches_count, > + struct devlink_dpipe_hfield *actions, > + unsigned int actions_count, > + bool counter_control_extern); > +void devlink_dpipe_table_unregister(struct devlink *devlink, > + const char *table_name); > +int devlink_dpipe_headers_register(struct devlink *devlink, > + struct devlink_dpipe_headers *dpipe_headers); > +void devlink_dpipe_headers_unregister(struct devlink *devlink); > +bool devlink_dpipe_table_counter_enabled(struct devlink *devlink, > + const char *table_name); > +int devlink_dpipe_entry_prepare_ctx(struct devlink_dpipe_dump_ctx *dump_ctx); > +int devlink_dpipe_entry_append_ctx(struct devlink_dpipe_dump_ctx *dump_ctx, > + struct devlink_dpipe_entry *entry); > +int devlink_dpipe_entry_close_ctx(struct devlink_dpipe_dump_ctx *dump_ctx); > #else > > static inline struct devlink *devlink_alloc(const struct devlink_ops *ops, > @@ -200,6 +372,56 @@ static inline void devlink_sb_unregister(struct devlink > *devlink, > { > } > > +int devlink_dpipe_table_register(struct devlink *devlink, > + const char *table_name, > + struct devlink_dpipe_table_ops *table_ops, > + void *priv, > + struct devlink_dpipe_hfield *matches, > + unsigned int matches_count, > + struct devlink_dpipe_hfield *actions, > + unsigned int actions_count, > + bool counter_control_extern) > +{ > + return 0; > +} > + > +void devlink_dpipe_table_unregister(struct devlink *devlink, > + const char *table_name); > +{ > +} > + > +int devlink_dpipe_headers_register(struct devlink *devlink, > + struct devlink_dpipe_headers *dpipe_headers); > +{ > + return 0; > +} > + > +void devlink_dpipe_headers_unregister(struct devlink *devlink) > +{ > +} > + > +bool devlink_dpipe_table_counter_enabled(struct devlink *devlink, > + const char *table_name) > +{ > + return false; > +} > + > +int devlink_dpipe_entry_prepare_ctx(struct devlink_dpipe_dump_ctx *dump_ctx) > +{ > + return 0; > +} > + > +int devlink_dpipe_entry_append_ctx(struct devlink_dpipe_dump_ctx *dump_ctx, > + struct devlink_dpipe_entry *entry) > +{ > + return 0; > +} > + > +int devlink_dpipe_entry_close_ctx(struct devlink_dpipe_dump_ctx *dump_ctx) > +{ > + return 0; > +} > + > #endif > > #endif /* _NET_DEVLINK_H_ */ > diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h > index 0f1f3a1..bbab55d 100644 > --- a/include/uapi/linux/devlink.h > +++ b/include/uapi/linux/devlink.h > @@ -65,8 +65,12 @@ enum devlink_command { > #define DEVLINK_CMD_ESWITCH_MODE_SET /* obsolete, never use this! */ \ > DEVLINK_CMD_ESWITCH_SET > > - /* add new commands above here */ > + DEVLINK_CMD_DPIPE_TABLES_GET, > + DEVLINK_CMD_DPIPE_ENTRIES_GET, > + DEVLINK_CMD_DPIPE_HEADERS_GET, > + DEVLINK_CMD_DPIPE_TABLE_COUNTER_SET, > > + /* add new commands above here */ > __DEVLINK_CMD_MAX, > DEVLINK_CMD_MAX = __DEVLINK_CMD_MAX - 1 > }; > @@ -148,10 +152,54 @@ enum devlink_attr { > DEVLINK_ATTR_ESWITCH_MODE, /* u16 */ > DEVLINK_ATTR_ESWITCH_INLINE_MODE, /* u8 */ > > + DEVLINK_ATTR_DPIPE_TABLES, /* nested */ > + DEVLINK_ATTR_DPIPE_TABLE, /* nested */ > + DEVLINK_ATTR_DPIPE_TABLE_NAME, /* string */ > + DEVLINK_ATTR_DPIPE_TABLE_SIZE, /* u32 */ > + DEVLINK_ATTR_DPIPE_TABLE_MATCHES, /* nested */ > + DEVLINK_ATTR_DPIPE_TABLE_ACTIONS, /* nested */ > + DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED, /* flag */ > + > + DEVLINK_ATTR_DPIPE_HFIELD, /* nested */ > + DEVLINK_ATTR_DPIPE_HFIELD_VALUE, > + DEVLINK_ATTR_DPIPE_HFIELD_MASK, > + DEVLINK_ATTR_DPIPE_HFIELD_MAPPING, /* u32 */ > + > + DEVLINK_ATTR_DPIPE_ENTRIES, /* nested */ > + DEVLINK_ATTR_DPIPE_ENTRY, /* nested */ > + DEVLINK_ATTR_DPIPE_ENTRY_INDEX, /* u32 */ > + DEVLINK_ATTR_DPIPE_ENTRY_MATCHES, /* nested */ > + DEVLINK_ATTR_DPIPE_ENTRY_ACTIONS, /* nested */ > + DEVLINK_ATTR_DPIPE_ENTRY_COUNTER, /* u64 */ > + DEVLINK_ATTR_DPIPE_ENTRY_COUNTER_VALID, /* u32 */ > + > + DEVLINK_ATTR_DPIPE_HEADERS, /* nested */ > + DEVLINK_ATTR_DPIPE_HEADER, /* nested */ > + DEVLINK_ATTR_DPIPE_HEADER_NAME, /* string */ > + DEVLINK_ATTR_DPIPE_HEADER_ID, /* u32 */ > + DEVLINK_ATTR_DPIPE_HEADER_FIELDS, /* string */ > + DEVLINK_ATTR_DPIPE_HEADER_GLOBAL, /* flag */ > + > + DEVLINK_ATTR_DPIPE_FIELD, /* nested */ > + DEVLINK_ATTR_DPIPE_FIELD_NAME, /* string */ > + DEVLINK_ATTR_DPIPE_FIELD_ID, /* u32 */ > + DEVLINK_ATTR_DPIPE_FIELD_BITWIDTH, /* u32 */ > + DEVLINK_ATTR_DPIPE_FIELD_MAPPING_TYPE, /* u32 */ > + > + DEVLINK_ATTR_PAD, > + > /* add new attributes above here, update the policy in devlink.c */ > > __DEVLINK_ATTR_MAX, > DEVLINK_ATTR_MAX = __DEVLINK_ATTR_MAX - 1 > }; > > +/* Mapping between internal resource described by the field and system > + * structure > + */ > +enum devlink_dpipe_field_mapping_type { > + DEVLINK_DPIPE_FIELD_MAPPING_TYPE_NONE, > + DEVLINK_DPIPE_FIELD_MAPPING_TYPE_IFINDEX, > +}; > + > #endif /* _UAPI_LINUX_DEVLINK_H_ */ > diff --git a/net/core/devlink.c b/net/core/devlink.c > index e9c1e6a..286d7d2 100644 > --- a/net/core/devlink.c > +++ b/net/core/devlink.c > @@ -1493,8 +1493,588 @@ static int devlink_nl_cmd_eswitch_set_doit(struct > sk_buff *skb, > if (err) > return err; > } > + return 0; > +} > + > +static int devlink_dpipe_hfield_put(struct sk_buff *skb, > + struct devlink_dpipe_hfield *hfield) > +{ > + struct devlink_dpipe_header *header = hfield->header; > + struct devlink_dpipe_field *field = &header->fields[hfield->field_id]; > + > + if (nla_put_u32(skb, DEVLINK_ATTR_DPIPE_HEADER_ID, header->id) || > + nla_put_u32(skb, DEVLINK_ATTR_DPIPE_FIELD_ID, field->id)) > + return -EMSGSIZE; > + > + if (header->global) > + if (nla_put_flag(skb, DEVLINK_ATTR_DPIPE_HEADER_GLOBAL)) > + return -EMSGSIZE; > + return 0; > +} > + > +static int devlink_dpipe_hfield_val_put(struct sk_buff *skb, > + struct devlink_dpipe_hfield_val *val) > +{ > + struct devlink_dpipe_header *header; > + struct devlink_dpipe_field *field; > + > + if (!val->hfield) > + return -EINVAL; > + if (devlink_dpipe_hfield_put(skb, val->hfield)) > + return -EMSGSIZE; > + if (nla_put(skb, DEVLINK_ATTR_DPIPE_HFIELD_VALUE, > + val->value_size, val->value)) > + return -EMSGSIZE; > + if (val->mask) > + if (nla_put(skb, DEVLINK_ATTR_DPIPE_HFIELD_MASK, > + val->value_size, val->mask)) > + return -EMSGSIZE; > + > + header = val->hfield->header; > + field = &header->fields[val->hfield->field_id]; > + if (field->mapping_type) > + if (nla_put_u32(skb, DEVLINK_ATTR_DPIPE_HFIELD_MAPPING, > + val->mapping_value)) > + return -EMSGSIZE; > + return 0; > +} > + > +static int devlink_dpipe_hfield_vals_put(struct sk_buff *skb, > + struct devlink_dpipe_hfield_val *vals, > + unsigned int vals_count) > +{ > + struct nlattr *hfield_attr; > + int i; > + int err; > + > + for (i = 0; i < vals_count; i++) { > + hfield_attr = nla_nest_start(skb, DEVLINK_ATTR_DPIPE_HFIELD); > + if (!hfield_attr) > + return -EMSGSIZE; > + err = devlink_dpipe_hfield_val_put(skb, &vals[i]); > + if (err) > + goto err_hfield_val_put; > + nla_nest_end(skb, hfield_attr); > + } > + return 0; > + > +err_hfield_val_put: > + nla_nest_cancel(skb, hfield_attr); > + return err; > +} > + > +static int devlink_dpipe_hfields_put(struct sk_buff *skb, > + struct devlink_dpipe_hfield *hfields, > + unsigned int hfields_count) > +{ > + struct nlattr *hfield_attr; > + int i; > + > + for (i = 0; i < hfields_count; i++) { > + hfield_attr = nla_nest_start(skb, DEVLINK_ATTR_DPIPE_HFIELD); > + if (!hfield_attr) > + return -EMSGSIZE; > + if (devlink_dpipe_hfield_put(skb, &hfields[i])) > + goto nla_put_failure; > + nla_nest_end(skb, hfield_attr); > + } > + return 0; > + > +nla_put_failure: > + nla_nest_cancel(skb, hfield_attr); > + return -EMSGSIZE; > +} > + > +static int devlink_dpipe_table_put(struct sk_buff *skb, > + struct devlink_dpipe_table *table) > +{ > + struct nlattr *matches_attr, *actions_attr, *table_attr; > + > + table_attr = nla_nest_start(skb, DEVLINK_ATTR_DPIPE_TABLE); > + if (!table_attr) > + return -EMSGSIZE; > + > + if (nla_put_string(skb, DEVLINK_ATTR_DPIPE_TABLE_NAME, table->name) || > + nla_put_u32(skb, DEVLINK_ATTR_DPIPE_TABLE_SIZE, table->size)) > + goto nla_put_failure; > + if (table->counters_enabled) > + if (nla_put_flag(skb, > + DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED)) > + goto nla_put_failure; > + > + matches_attr = nla_nest_start(skb, DEVLINK_ATTR_DPIPE_TABLE_MATCHES); > + if (!matches_attr) > + goto nla_put_failure; > + > + if (devlink_dpipe_hfields_put(skb, table->matches, > + table->matches_count)) { > + nla_nest_cancel(skb, matches_attr); > + goto nla_put_failure; > + } > + > + actions_attr = nla_nest_start(skb, DEVLINK_ATTR_DPIPE_TABLE_ACTIONS); > + if (!actions_attr) > + goto nla_put_failure; > + > + if (devlink_dpipe_hfields_put(skb, table->actions, > + table->actions_count)) { > + nla_nest_cancel(skb, actions_attr); > + goto nla_put_failure; > + } > + > + nla_nest_end(skb, actions_attr); > + nla_nest_end(skb, table_attr); > + return 0; > + > +nla_put_failure: > + nla_nest_cancel(skb, table_attr); > + return -EMSGSIZE; > +} > + > +static int devlink_dpipe_send_and_alloc_skb(struct sk_buff **pskb, > + struct genl_info *info) > +{ > + int err; > + > + if (*pskb) { > + err = genlmsg_reply(*pskb, info); > + if (err) > + return err; > + } > + *pskb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL); > + if (!*pskb) > + return -ENOMEM; > + return 0; > +} > + > +static int devlink_dpipe_tables_fill(struct genl_info *info, > + enum devlink_command cmd, int flags, > + struct list_head *dpipe_tables) > + > +{ > + struct devlink *devlink = info->user_ptr[0]; > + struct devlink_dpipe_table *table; > + struct nlattr *tables_attr; > + struct sk_buff *skb = NULL; > + struct nlmsghdr *nlh; > + bool incomplete; > + void *hdr; > + int i; > + int err; > + > + table = list_first_entry(dpipe_tables, > + struct devlink_dpipe_table, list); > +start_again: > + err = devlink_dpipe_send_and_alloc_skb(&skb, info); > + if (err) > + return err; > + > + hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq, > + &devlink_nl_family, NLM_F_MULTI, cmd); > + if (!hdr) > + return -EMSGSIZE; > > + if (devlink_nl_put_handle(skb, devlink)) > + goto nla_put_failure; > + tables_attr = nla_nest_start(skb, DEVLINK_ATTR_DPIPE_TABLES); > + if (!tables_attr) > + goto nla_put_failure; > + > + i = 0; > + incomplete = false; > + list_for_each_entry_from(table, dpipe_tables, list) { > + err = devlink_dpipe_table_put(skb, table); > + if (err) { > + if (!i) > + goto err_table_put; > + incomplete = true; > + break; > + } > + i++; > + } > + > + nla_nest_end(skb, tables_attr); > + genlmsg_end(skb, hdr); > + if (incomplete) > + goto start_again; > + > +send_done: > + nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq, > + NLMSG_DONE, 0, flags | NLM_F_MULTI); > + if (!nlh) { > + err = devlink_dpipe_send_and_alloc_skb(&skb, info); > + if (err) > + goto err_skb_send_alloc; > + goto send_done; > + } > + > + return genlmsg_reply(skb, info); > + > +nla_put_failure: > + err = -EMSGSIZE; > +err_table_put: > +err_skb_send_alloc: > + genlmsg_cancel(skb, hdr); > + nlmsg_free(skb); > + return err; > +} > + > +static int devlink_nl_cmd_dpipe_tables_get(struct sk_buff *skb, > + struct genl_info *info) > +{ > + struct devlink *devlink = info->user_ptr[0]; > + > + return devlink_dpipe_tables_fill(info, DEVLINK_CMD_DPIPE_TABLES_GET, 0, > + &devlink->dpipe_table_list); > +} > + > +static int devlink_dpipe_entry_put(struct sk_buff *skb, > + struct devlink_dpipe_entry *entry) > +{ > + struct nlattr *entry_attr, *matches_attr, *actions_attr; > + int err; > + > + entry_attr = nla_nest_start(skb, DEVLINK_ATTR_DPIPE_ENTRY); > + if (!entry_attr) > + return -EMSGSIZE; > + > + if (nla_put_u32(skb, DEVLINK_ATTR_DPIPE_ENTRY_INDEX, entry->index)) > + goto nla_put_failure; > + if (entry->counter_valid) > + if (nla_put_u64_64bit(skb, DEVLINK_ATTR_DPIPE_ENTRY_COUNTER, > + entry->counter, DEVLINK_ATTR_PAD)) > + goto nla_put_failure; > + > + matches_attr = nla_nest_start(skb, DEVLINK_ATTR_DPIPE_ENTRY_MATCHES); > + if (!matches_attr) > + goto nla_put_failure; > + > + err = devlink_dpipe_hfield_vals_put(skb, entry->matches, > + entry->matches_count); > + if (err) { > + nla_nest_cancel(skb, matches_attr); > + goto err_hfield_vals_put; > + } > + nla_nest_end(skb, matches_attr); > + > + actions_attr = nla_nest_start(skb, DEVLINK_ATTR_DPIPE_ENTRY_ACTIONS); > + if (!actions_attr) > + goto nla_put_failure; > + > + err = devlink_dpipe_hfield_vals_put(skb, entry->actions, > + entry->actions_count); > + if (err) { > + nla_nest_cancel(skb, actions_attr); > + goto err_hfield_vals_put; > + } > + nla_nest_end(skb, actions_attr); > + > + nla_nest_end(skb, entry_attr); > return 0; > + > +nla_put_failure: > + err = -EMSGSIZE; > +err_hfield_vals_put: > + nla_nest_cancel(skb, entry_attr); > + return err; > +} > + > +static struct devlink_dpipe_table * > +devlink_dpipe_table_find(struct list_head *dpipe_tables, > + const char *table_name) > +{ > + struct devlink_dpipe_table *table; > + > + list_for_each_entry_rcu(table, dpipe_tables, list) { > + if (!strcmp(table->name, table_name)) > + return table; > + } > + return NULL; > +} > + > +int devlink_dpipe_entry_prepare_ctx(struct devlink_dpipe_dump_ctx *dump_ctx) > +{ > + struct devlink *devlink; > + int err; > + > + err = devlink_dpipe_send_and_alloc_skb(&dump_ctx->skb, > + dump_ctx->info); > + if (err) > + return err; > + > + dump_ctx->hdr = genlmsg_put(dump_ctx->skb, > + dump_ctx->info->snd_portid, > + dump_ctx->info->snd_seq, > + &devlink_nl_family, NLM_F_MULTI, > + dump_ctx->cmd); > + if (!dump_ctx->hdr) > + goto nla_put_failure; > + > + devlink = dump_ctx->info->user_ptr[0]; > + if (devlink_nl_put_handle(dump_ctx->skb, devlink)) > + goto nla_put_failure; > + dump_ctx->nest = nla_nest_start(dump_ctx->skb, > + DEVLINK_ATTR_DPIPE_ENTRIES); > + if (!dump_ctx->nest) > + goto nla_put_failure; > + return 0; > + > +nla_put_failure: > + genlmsg_cancel(dump_ctx->skb, dump_ctx->hdr); > + nlmsg_free(dump_ctx->skb); > + return -EMSGSIZE; > +} > + > +int devlink_dpipe_entry_append_ctx(struct devlink_dpipe_dump_ctx *dump_ctx, > + struct devlink_dpipe_entry *entry) > +{ > + return devlink_dpipe_entry_put(dump_ctx->skb, entry); > +} > + > +int devlink_dpipe_entry_close_ctx(struct devlink_dpipe_dump_ctx *dump_ctx) > +{ > + nla_nest_end(dump_ctx->skb, dump_ctx->nest); > + genlmsg_end(dump_ctx->skb, dump_ctx->hdr); > + return 0; > +} > + > +static int devlink_dpipe_entries_fill(struct genl_info *info, > + enum devlink_command cmd, int flags, > + struct devlink_dpipe_table *table) > +{ > + struct devlink_dpipe_dump_ctx dump_ctx; > + struct nlmsghdr *nlh; > + int err; > + > + dump_ctx.skb = NULL; > + dump_ctx.cmd = cmd; > + dump_ctx.info = info; > + > + err = table->table_ops->entries_dump(table->priv, > + table->counters_enabled, > + &dump_ctx); > + if (err) > + goto err_entries_dump; > + > +send_done: > + nlh = nlmsg_put(dump_ctx.skb, info->snd_portid, info->snd_seq, > + NLMSG_DONE, 0, flags | NLM_F_MULTI); > + if (!nlh) { > + err = devlink_dpipe_send_and_alloc_skb(&dump_ctx.skb, info); > + if (err) > + goto err_skb_send_alloc; > + goto send_done; > + } > + return genlmsg_reply(dump_ctx.skb, info); > + > +err_entries_dump: > +err_skb_send_alloc: > + genlmsg_cancel(dump_ctx.skb, dump_ctx.hdr); > + nlmsg_free(dump_ctx.skb); > + return err; > +} > + > +static int devlink_nl_cmd_dpipe_entries_get(struct sk_buff *skb, > + struct genl_info *info) > +{ > + struct devlink *devlink = info->user_ptr[0]; > + struct devlink_dpipe_table *table; > + const char *table_name; > + > + if (!info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME]) > + return -EINVAL; > + > + table_name = nla_data(info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME]); > + table = devlink_dpipe_table_find(&devlink->dpipe_table_list, > + table_name); > + if (!table) > + return -EINVAL; > + > + if (!table->table_ops->entries_dump) > + return -EINVAL; > + > + return devlink_dpipe_entries_fill(info, DEVLINK_CMD_DPIPE_ENTRIES_GET, > + 0, table); > +} > + > +static int devlink_dpipe_fields_put(struct sk_buff *skb, > + const struct devlink_dpipe_header *header) > +{ > + struct devlink_dpipe_field *field; > + struct nlattr *field_attr; > + int i; > + > + for (i = 0; i < header->fields_count; i++) { > + field = &header->fields[i]; > + field_attr = nla_nest_start(skb, DEVLINK_ATTR_DPIPE_FIELD); > + if (!field_attr) > + return -EMSGSIZE; > + if (nla_put_string(skb, DEVLINK_ATTR_DPIPE_FIELD_NAME, > field->name) || > + nla_put_u32(skb, DEVLINK_ATTR_DPIPE_FIELD_ID, field->id) || > + nla_put_u32(skb, DEVLINK_ATTR_DPIPE_FIELD_BITWIDTH, > field->bitwidth)) > + goto nla_put_failure; > + if (field->mapping_type) > + if (nla_put_u32(skb, > + DEVLINK_ATTR_DPIPE_FIELD_MAPPING_TYPE, > + field->mapping_type)) > + goto nla_put_failure; > + nla_nest_end(skb, field_attr); > + } > + return 0; > + > +nla_put_failure: > + nla_nest_cancel(skb, field_attr); > + return -EMSGSIZE; > +} > + > +static int devlink_dpipe_header_put(struct sk_buff *skb, > + struct devlink_dpipe_header *header) > +{ > + struct nlattr *fields_attr, *header_attr; > + int err; > + > + header_attr = nla_nest_start(skb, DEVLINK_ATTR_DPIPE_HEADER); > + if (!header) > + return -EMSGSIZE; > + > + if (nla_put_string(skb, DEVLINK_ATTR_DPIPE_HEADER_NAME, header->name) || > + nla_put_u32(skb, DEVLINK_ATTR_DPIPE_HEADER_ID, header->id)) > + goto nla_put_failure; > + > + fields_attr = nla_nest_start(skb, DEVLINK_ATTR_DPIPE_HEADER_FIELDS); > + if (!fields_attr) > + goto nla_put_failure; > + > + err = devlink_dpipe_fields_put(skb, header); > + if (err) { > + nla_nest_cancel(skb, fields_attr); > + goto nla_put_failure; > + } > + nla_nest_end(skb, fields_attr); > + nla_nest_end(skb, header_attr); > + return 0; > + > +nla_put_failure: > + err = -EMSGSIZE; > + nla_nest_cancel(skb, header_attr); > + return err; > +} > + > +static int devlink_dpipe_headers_fill(struct genl_info *info, > + enum devlink_command cmd, int flags, > + struct devlink_dpipe_headers * > + dpipe_headers) > +{ > + struct devlink *devlink = info->user_ptr[0]; > + struct nlattr *headers_attr; > + struct sk_buff *skb = NULL; > + struct nlmsghdr *nlh; > + void *hdr; > + int i, j; > + int err; > + > + i = 0; > +start_again: > + err = devlink_dpipe_send_and_alloc_skb(&skb, info); > + if (err) > + return err; > + > + hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq, > + &devlink_nl_family, NLM_F_MULTI, cmd); > + if (!hdr) > + return -EMSGSIZE; > + > + if (devlink_nl_put_handle(skb, devlink)) > + goto nla_put_failure; > + headers_attr = nla_nest_start(skb, DEVLINK_ATTR_DPIPE_HEADERS); > + if (!headers_attr) > + goto nla_put_failure; > + > + j = 0; > + for (; i < dpipe_headers->headers_count; i++) { > + err = devlink_dpipe_header_put(skb, dpipe_headers->headers[i]); > + if (err) { > + if (!j) > + goto err_table_put; > + break; > + } > + j++; > + } > + nla_nest_end(skb, headers_attr); > + genlmsg_end(skb, hdr); > + if (i != dpipe_headers->headers_count) > + goto start_again; > + > +send_done: > + nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq, > + NLMSG_DONE, 0, flags | NLM_F_MULTI); > + if (!nlh) { > + err = devlink_dpipe_send_and_alloc_skb(&skb, info); > + if (err) > + goto err_skb_send_alloc; > + goto send_done; > + } > + return genlmsg_reply(skb, info); > + > +nla_put_failure: > + err = -EMSGSIZE; > +err_table_put: > +err_skb_send_alloc: > + genlmsg_cancel(skb, hdr); > + nlmsg_free(skb); > + return err; > +} > + > +static int devlink_nl_cmd_dpipe_headers_get(struct sk_buff *skb, > + struct genl_info *info) > +{ > + struct devlink *devlink = info->user_ptr[0]; > + > + return devlink_dpipe_headers_fill(info, DEVLINK_CMD_DPIPE_HEADERS_GET, > + 0, devlink->dpipe_headers); > +} > + > +static int devlink_dpipe_table_counter_set(struct devlink *devlink, > + const char *table_name, > + bool enable) > +{ > + struct devlink_dpipe_table *table; > + > + table = devlink_dpipe_table_find(&devlink->dpipe_table_list, > + table_name); > + if (!table) > + return -EINVAL; > + > + if (table->counter_control_extern) > + return -EOPNOTSUPP; > + > + if (!(table->counters_enabled ^ enable)) > + return 0; > + > + table->counters_enabled = enable; > + if (table->table_ops->counter_set_update) > + table->table_ops->counter_set_update(table->priv, enable); > + return 0; > +} > + > +static int devlink_nl_cmd_dpipe_table_counter_set(struct sk_buff *skb, > + struct genl_info *info) > +{ > + struct devlink *devlink = info->user_ptr[0]; > + const char *table_name; > + int counter_enable; > + > + if (!info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME]) > + return -EINVAL; > + table_name = nla_data(info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME]); > + > + if (!info->attrs[DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED]) > + counter_enable = false; > + else > + counter_enable = true; > + > + return devlink_dpipe_table_counter_set(devlink, table_name, > + counter_enable); > } > > static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = { > @@ -1512,6 +2092,8 @@ static const struct nla_policy > devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = { > [DEVLINK_ATTR_SB_TC_INDEX] = { .type = NLA_U16 }, > [DEVLINK_ATTR_ESWITCH_MODE] = { .type = NLA_U16 }, > [DEVLINK_ATTR_ESWITCH_INLINE_MODE] = { .type = NLA_U8 }, > + [DEVLINK_ATTR_DPIPE_TABLE_NAME] = { .type = NLA_NUL_STRING }, > + [DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED] = { .type = NLA_U32 }, > }; > > static const struct genl_ops devlink_nl_ops[] = { > @@ -1644,6 +2226,34 @@ static const struct genl_ops devlink_nl_ops[] = { > .flags = GENL_ADMIN_PERM, > .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK, > }, > + { > + .cmd = DEVLINK_CMD_DPIPE_TABLES_GET, > + .doit = devlink_nl_cmd_dpipe_tables_get, > + .policy = devlink_nl_policy, > + .flags = GENL_ADMIN_PERM, > + .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK, > + }, > + { > + .cmd = DEVLINK_CMD_DPIPE_ENTRIES_GET, > + .doit = devlink_nl_cmd_dpipe_entries_get, > + .policy = devlink_nl_policy, > + .flags = GENL_ADMIN_PERM, > + .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK, > + }, > + { > + .cmd = DEVLINK_CMD_DPIPE_HEADERS_GET, > + .doit = devlink_nl_cmd_dpipe_headers_get, > + .policy = devlink_nl_policy, > + .flags = GENL_ADMIN_PERM, > + .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK, > + }, > + { > + .cmd = DEVLINK_CMD_DPIPE_TABLE_COUNTER_SET, > + .doit = devlink_nl_cmd_dpipe_table_counter_set, > + .policy = devlink_nl_policy, > + .flags = GENL_ADMIN_PERM, > + .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK, > + }, > }; > > static struct genl_family devlink_nl_family __ro_after_init = { > @@ -1680,6 +2290,7 @@ struct devlink *devlink_alloc(const struct devlink_ops > *ops, size_t priv_size) > devlink_net_set(devlink, &init_net); > INIT_LIST_HEAD(&devlink->port_list); > INIT_LIST_HEAD(&devlink->sb_list); > + INIT_LIST_HEAD_RCU(&devlink->dpipe_table_list); > return devlink; > } > EXPORT_SYMBOL_GPL(devlink_alloc); > @@ -1880,6 +2491,142 @@ void devlink_sb_unregister(struct devlink *devlink, > unsigned int sb_index) > } > EXPORT_SYMBOL_GPL(devlink_sb_unregister); > > +/** > + * devlink_dpipe_headers_register - register dpipe headers > + * > + * @devlink: devlink > + * @dpipe_headers: dpipe header array > + * > + * Register the headers supported by hardware. > + */ > +int devlink_dpipe_headers_register(struct devlink *devlink, > + struct devlink_dpipe_headers *dpipe_headers) > +{ > + mutex_lock(&devlink_mutex); > + devlink->dpipe_headers = dpipe_headers; > + mutex_unlock(&devlink_mutex); > + return 0; > +} > +EXPORT_SYMBOL_GPL(devlink_dpipe_headers_register); > + > +/** > + * devlink_dpipe_headers_unregister - unregister dpipe headers > + * > + * @devlink: devlink > + * > + * Unregister the headers supported by hardware. > + */ > +void devlink_dpipe_headers_unregister(struct devlink *devlink) > +{ > + mutex_lock(&devlink_mutex); > + devlink->dpipe_headers = NULL; > + mutex_unlock(&devlink_mutex); > +} > +EXPORT_SYMBOL_GPL(devlink_dpipe_headers_unregister); > + > +/** > + * devlink_dpipe_table_counter_enabled - check if counter allocation > + * required > + * @devlink: devlink > + * @table_name: tables name > + * > + * Used by driver to check if counter allocation is required. > + * After counter allocation is turned on the table entries > + * are updated to include counter statistics. > + * > + * After that point on the driver must respect the counter > + * state so that each entry added to the table is added > + * with a counter. > + */ > +bool devlink_dpipe_table_counter_enabled(struct devlink *devlink, > + const char *table_name) > +{ > + struct devlink_dpipe_table *table; > + bool enabled; > + > + rcu_read_lock(); > + table = devlink_dpipe_table_find(&devlink->dpipe_table_list, > + table_name); > + enabled = false; > + if (table) > + enabled = table->counters_enabled; > + rcu_read_unlock(); > + return enabled; > +} > +EXPORT_SYMBOL_GPL(devlink_dpipe_table_counter_enabled); > + > +/** > + * devlink_dpipe_table_register - register dpipe table > + * > + * @devlink: devlink > + * @table_name: table name > + * @table_ops: table ops > + * @priv: priv > + * @matches: match tuples > + * @matches_count: count of matches count > + * @actions: action tuples > + * @actions_count: count of action tuples > + * @counter_control_extern: external control for counters > + */ > +int devlink_dpipe_table_register(struct devlink *devlink, > + const char *table_name, > + struct devlink_dpipe_table_ops *table_ops, > + void *priv, > + struct devlink_dpipe_hfield *matches, > + unsigned int matches_count, > + struct devlink_dpipe_hfield *actions, > + unsigned int actions_count, > + bool counter_control_extern) > +{ > + struct devlink_dpipe_table *table; > + > + if (devlink_dpipe_table_find(&devlink->dpipe_table_list, table_name)) > + return -EEXIST; > + > + table = kzalloc(sizeof(*table), GFP_KERNEL); > + if (!table) > + return -ENOMEM; > + > + table->name = table_name; > + table->table_ops = table_ops; > + table->priv = priv; > + table->matches = matches; > + table->matches_count = matches_count; > + table->actions = actions; > + table->actions_count = actions_count; > + table->counter_control_extern = counter_control_extern; > + mutex_lock(&devlink_mutex); > + list_add_tail_rcu(&table->list, &devlink->dpipe_table_list); > + mutex_unlock(&devlink_mutex); > + return 0; > +} > +EXPORT_SYMBOL_GPL(devlink_dpipe_table_register); > + > +/** > + * devlink_dpipe_table_unregister - unregister dpipe table > + * > + * @devlink: devlink > + * @table_name: table name > + */ > +void devlink_dpipe_table_unregister(struct devlink *devlink, > + const char *table_name) > +{ > + struct devlink_dpipe_table *table; > + > + mutex_lock(&devlink_mutex); > + table = devlink_dpipe_table_find(&devlink->dpipe_table_list, > + table_name); > + if (!table) > + goto unlock; > + list_del_rcu(&table->list); > + mutex_unlock(&devlink_mutex); > + kfree_rcu(table, rcu); > + return; > +unlock: > + mutex_unlock(&devlink_mutex); > +} > +EXPORT_SYMBOL_GPL(devlink_dpipe_table_unregister); > + > static int __init devlink_module_init(void) > { > return genl_register_family(&devlink_nl_family); >