All of lore.kernel.org
 help / color / mirror / Atom feed
* [patch net-next 0/8] Add support for pipeline debug (dpipe)
@ 2017-03-25  7:35 Jiri Pirko
  2017-03-25  7:35 ` [patch net-next 1/8] devlink: Support " Jiri Pirko
                   ` (9 more replies)
  0 siblings, 10 replies; 20+ messages in thread
From: Jiri Pirko @ 2017-03-25  7:35 UTC (permalink / raw)
  To: netdev
  Cc: davem, arkadis, idosch, mlxsw, jhs, ivecera, roopa, f.fainelli,
	vivien.didelot, john.fastabend, andrew, simon.horman

From: Jiri Pirko <jiri@mellanox.com>

Arkadi says:

While doing the hardware offloading process much of the hardware
specifics cannot be presented. An example for such is the routing
LPM algorithm which differ in hardware implementation from the
kernel software implementation. The only information the user receives
is whether specific route is offloaded or not, but he cannot really
understand the underlying implementation nor get the specific statistics
related to that process.

Another example is ACL offload using TC which is commonly implemented
using TCAM memory. Currently there is no capability to gain visibility
into the TCAM structure and to debug suboptimal resource allocation.

This patchset introduces capability for exporting the ASICs pipeline
abstraction via devlink infrastructure, which should serve as an
complementary tool. This infrastructure allows the user to get visibility
into the ASIC by modeling it as a set of match/action tables.

The main objects defined:
Table - abstraction for a single pipeline stage. Contains the
        available match/actions and counter availability.
Entry - entry in a specific table with specific matches/actions
        values and dedicated counter.
Header/field - tuples which describes the tables behavior.

As an example one of the ASIC's L3 blocks will be modeled. The egress
rif (router interface) table is the final step in the L3 pipeline
processing which does match on the internal rif index which was
determined before by the routing logic. The erif table determines
whether to forward or drop the packet and updates the corresponding
rif L3 statistics.

To expose this internal resources a special metadata header will
be introduced that describes the internal information gathered by
the ASIC's pipeline and contains the following fields: rif_port_index,
forward and drop.

Some internal hardware resources have direct mapping to kernel
objects. For example the rif_port_index is mapped to the net-devices
ifindex. By providing this mapping the users gains visibility into
the offloading process.

Follow-up work will include exporting more L3 tables which will give
visibility into the routing process.

First stage is adding support for dpipe in devlink. Next add support
in spectrum driver. Finally implement egress router interface
(erif) table for spectrum ASIC as an example.

Arkadi Sharshevsky (8):
  devlink: Support for pipeline debug (dpipe)
  mlxsw: reg: Add counter fields to RITR register
  mlxsw: spectrum: Add placeholder for dpipe
  mlxsw: spectrum: Add definition for egress rif table
  mlxsw: reg: Add Router Interface Counter Register
  mlxsw: spectrum: Support for counters on router interfaces
  mlxsw: spectrum_router: Add rif helper functions
  mlxsw: spectrum: Add Support for erif table entries access

 drivers/net/ethernet/mellanox/mlxsw/Makefile       |   2 +-
 drivers/net/ethernet/mellanox/mlxsw/reg.h          | 178 +++++
 drivers/net/ethernet/mellanox/mlxsw/resources.h    |   2 +
 drivers/net/ethernet/mellanox/mlxsw/spectrum.c     |  10 +
 drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c |   9 +
 drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.h |   1 +
 .../net/ethernet/mellanox/mlxsw/spectrum_dpipe.c   | 351 +++++++++
 .../net/ethernet/mellanox/mlxsw/spectrum_dpipe.h   |  43 ++
 .../net/ethernet/mellanox/mlxsw/spectrum_router.c  | 170 +++++
 .../net/ethernet/mellanox/mlxsw/spectrum_router.h  |  58 ++
 include/net/devlink.h                              | 259 +++++++
 include/uapi/linux/devlink.h                       |  67 +-
 net/core/devlink.c                                 | 836 +++++++++++++++++++++
 13 files changed, 1984 insertions(+), 2 deletions(-)
 create mode 100644 drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c
 create mode 100644 drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h
 create mode 100644 drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h

-- 
2.7.4

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [patch net-next 1/8] devlink: Support for pipeline debug (dpipe)
  2017-03-25  7:35 [patch net-next 0/8] Add support for pipeline debug (dpipe) Jiri Pirko
@ 2017-03-25  7:35 ` Jiri Pirko
  2017-03-25  7:35 ` [patch net-next 2/8] mlxsw: reg: Add counter fields to RITR register Jiri Pirko
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: Jiri Pirko @ 2017-03-25  7:35 UTC (permalink / raw)
  To: netdev
  Cc: davem, arkadis, idosch, mlxsw, jhs, ivecera, roopa, f.fainelli,
	vivien.didelot, john.fastabend, andrew, simon.horman

From: Arkadi Sharshevsky <arkadis@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.

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.

Match - represent specific match rule. Can describe match on specific
        field or header. The header index should be specified as well
        in order to support several header instances of the same type
        (tunneling).

Action - represents specific action rule. Actions can describe operations
         on specific field values for example like set, increment, etc.
         And header operation like add and delete.

Value - represents value which can be associated with specific match or
        action.

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 values for match/action.

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.

Signed-off-by: Arkadi Sharshevsky <arkadis@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
---
 include/net/devlink.h        | 259 ++++++++++++++
 include/uapi/linux/devlink.h |  67 +++-
 net/core/devlink.c           | 836 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1161 insertions(+), 1 deletion(-)

diff --git a/include/net/devlink.h b/include/net/devlink.h
index d29e5fc..24de13f 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,178 @@ struct devlink_sb_pool_info {
 	enum devlink_sb_threshold_type threshold_type;
 };
 
+/**
+ * struct devlink_dpipe_field - dpipe field object
+ * @name: field name
+ * @id: index inside the headers field 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: index, global/local detrmined by global bit
+ * @fields: fields
+ * @fields_count: number of fields
+ * @global: indicates if 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_match - represents match operation
+ * @type: type of match
+ * @header_index: header index (packets can have several headers of same
+ *		  type like in case of tunnels)
+ * @header: header
+ * @fieled_id: field index
+ */
+struct devlink_dpipe_match {
+	enum devlink_dpipe_match_type type;
+	unsigned int header_index;
+	struct devlink_dpipe_header *header;
+	unsigned int field_id;
+};
+
+/**
+ * struct devlink_dpipe_action - represents action operation
+ * @type: type of action
+ * @header_index: header index (packets can have several headers of same
+ *		  type like in case of tunnels)
+ * @header: header
+ * @fieled_id: field index
+ */
+struct devlink_dpipe_action {
+	enum devlink_dpipe_action_type type;
+	unsigned int header_index;
+	struct devlink_dpipe_header *header;
+	unsigned int field_id;
+};
+
+/**
+ * struct devlink_dpipe_value - represents value of match/action
+ * @action: action
+ * @match: match
+ * @mapping_value: in case the field has some mapping this value
+ *                 specified the mapping value
+ * @mapping_valid: specify if mapping value is valid
+ * @value_size: value size
+ * @value: value
+ * @mask: bit mask
+ */
+struct devlink_dpipe_value {
+	union {
+		struct devlink_dpipe_action *action;
+		struct devlink_dpipe_match *match;
+	};
+	unsigned int mapping_value;
+	bool mapping_valid;
+	unsigned int value_size;
+	void *value;
+	void *mask;
+};
+
+/**
+ * struct devlink_dpipe_entry - table entry object
+ * @index: index of the entry in the table
+ * @match_values: match values
+ * @matche_values_count: count of matches tuples
+ * @action_values: actions values
+ * @action_values_count: count of actions values
+ * @counter: value of counter
+ * @counter_valid: Specify if value is valid from hardware
+ */
+struct devlink_dpipe_entry {
+	u64 index;
+	struct devlink_dpipe_value *match_values;
+	unsigned int match_values_count;
+	struct devlink_dpipe_value *action_values;
+	unsigned int action_values_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
+ * @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;
+	u64 size;
+	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
+ * @actions_dump - dumps all tables actions
+ * @matches_dump - dumps all tables matches
+ * @entries_dump - dumps all active entries in the table
+ * @counters_set_update - when changing the counter status hardware sync
+ *			  maybe needed to allocate/free counter related
+ *			  resources
+ */
+struct devlink_dpipe_table_ops {
+	int (*actions_dump)(void *priv, struct sk_buff *skb);
+	int (*matches_dump)(void *priv, struct sk_buff *skb);
+	int (*entries_dump)(void *priv, bool counters_enabled,
+			    struct devlink_dpipe_dump_ctx *dump_ctx);
+	int (*counters_set_update)(void *priv, bool enable);
+};
+
+/**
+ * struct devlink_dpipe_headers - dpipe headers
+ * @headers - header array can be shared (global bit) or driver specific
+ * @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,6 +306,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, u64 size,
+				 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_ctx_prepare(struct devlink_dpipe_dump_ctx *dump_ctx);
+int devlink_dpipe_entry_ctx_append(struct devlink_dpipe_dump_ctx *dump_ctx,
+				   struct devlink_dpipe_entry *entry);
+int devlink_dpipe_entry_ctx_close(struct devlink_dpipe_dump_ctx *dump_ctx);
+int devlink_dpipe_action_put(struct sk_buff *skb,
+			     struct devlink_dpipe_action *action);
+int devlink_dpipe_match_put(struct sk_buff *skb,
+			    struct devlink_dpipe_match *match);
 
 #else
 
@@ -200,6 +394,71 @@ static inline void devlink_sb_unregister(struct devlink *devlink,
 {
 }
 
+static inline int
+devlink_dpipe_table_register(struct devlink *devlink,
+			     const char *table_name,
+			     struct devlink_dpipe_table_ops *table_ops,
+			     void *priv, u64 size,
+			     bool counter_control_extern)
+{
+	return 0;
+}
+
+static inline void devlink_dpipe_table_unregister(struct devlink *devlink,
+						  const char *table_name)
+{
+}
+
+static inline int devlink_dpipe_headers_register(struct devlink *devlink,
+						 struct devlink_dpipe_headers *
+						 dpipe_headers)
+{
+	return 0;
+}
+
+static inline void devlink_dpipe_headers_unregister(struct devlink *devlink)
+{
+}
+
+static inline bool devlink_dpipe_table_counter_enabled(struct devlink *devlink,
+						       const char *table_name)
+{
+	return false;
+}
+
+static inline int
+devlink_dpipe_entry_ctx_prepare(struct devlink_dpipe_dump_ctx *dump_ctx)
+{
+	return 0;
+}
+
+static inline int
+devlink_dpipe_entry_ctx_append(struct devlink_dpipe_dump_ctx *dump_ctx,
+			       struct devlink_dpipe_entry *entry)
+{
+	return 0;
+}
+
+static inline int
+devlink_dpipe_entry_ctx_close(struct devlink_dpipe_dump_ctx *dump_ctx)
+{
+	return 0;
+}
+
+static inline int
+devlink_dpipe_action_put(struct sk_buff *skb,
+			 struct devlink_dpipe_action *action)
+{
+	return 0;
+}
+
+static inline int
+devlink_dpipe_match_put(struct sk_buff *skb,
+			struct devlink_dpipe_match *match)
+{
+	return 0;
+}
+
 #endif
 
 #endif /* _NET_DEVLINK_H_ */
diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h
index 0f1f3a1..b47bee2 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_TABLE_GET,
+	DEVLINK_CMD_DPIPE_ENTRIES_GET,
+	DEVLINK_CMD_DPIPE_HEADERS_GET,
+	DEVLINK_CMD_DPIPE_TABLE_COUNTERS_SET,
 
+	/* add new commands above here */
 	__DEVLINK_CMD_MAX,
 	DEVLINK_CMD_MAX = __DEVLINK_CMD_MAX - 1
 };
@@ -148,10 +152,71 @@ 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,		/* u64 */
+	DEVLINK_ATTR_DPIPE_TABLE_MATCHES,	/* nested */
+	DEVLINK_ATTR_DPIPE_TABLE_ACTIONS,	/* nested */
+	DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED,	/* u8 */
+
+	DEVLINK_ATTR_DPIPE_ENTRIES,		/* nested */
+	DEVLINK_ATTR_DPIPE_ENTRY,		/* nested */
+	DEVLINK_ATTR_DPIPE_ENTRY_INDEX,		/* u64 */
+	DEVLINK_ATTR_DPIPE_ENTRY_MATCH_VALUES,	/* nested */
+	DEVLINK_ATTR_DPIPE_ENTRY_ACTION_VALUES,	/* nested */
+	DEVLINK_ATTR_DPIPE_ENTRY_COUNTER,	/* u64 */
+
+	DEVLINK_ATTR_DPIPE_MATCH,		/* nested */
+	DEVLINK_ATTR_DPIPE_MATCH_VALUE,		/* nested */
+	DEVLINK_ATTR_DPIPE_MATCH_TYPE,		/* u32 */
+
+	DEVLINK_ATTR_DPIPE_ACTION,		/* nested */
+	DEVLINK_ATTR_DPIPE_ACTION_VALUE,	/* nested */
+	DEVLINK_ATTR_DPIPE_ACTION_TYPE,		/* u32 */
+
+	DEVLINK_ATTR_DPIPE_VALUE,
+	DEVLINK_ATTR_DPIPE_VALUE_MASK,
+	DEVLINK_ATTR_DPIPE_VALUE_MAPPING,	/* 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,	/* nested */
+	DEVLINK_ATTR_DPIPE_HEADER_GLOBAL,	/* u8 */
+	DEVLINK_ATTR_DPIPE_HEADER_INDEX,	/* u32 */
+
+	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,
+};
+
+/* Match type - specify the type of the match */
+enum devlink_dpipe_match_type {
+	DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT,
+};
+
+/* Action type - specify the action type */
+enum devlink_dpipe_action_type {
+	DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY,
+};
+
 #endif /* _UAPI_LINUX_DEVLINK_H_ */
diff --git a/net/core/devlink.c b/net/core/devlink.c
index e9c1e6a..24b7660 100644
--- a/net/core/devlink.c
+++ b/net/core/devlink.c
@@ -1493,8 +1493,686 @@ static int devlink_nl_cmd_eswitch_set_doit(struct sk_buff *skb,
 		if (err)
 			return err;
 	}
+	return 0;
+}
+
+int devlink_dpipe_match_put(struct sk_buff *skb,
+			    struct devlink_dpipe_match *match)
+{
+	struct devlink_dpipe_header *header = match->header;
+	struct devlink_dpipe_field *field = &header->fields[match->field_id];
+	struct nlattr *match_attr;
+
+	match_attr = nla_nest_start(skb, DEVLINK_ATTR_DPIPE_MATCH);
+	if (!match_attr)
+		return -EMSGSIZE;
+
+	if (nla_put_u32(skb, DEVLINK_ATTR_DPIPE_MATCH_TYPE, match->type) ||
+	    nla_put_u32(skb, DEVLINK_ATTR_DPIPE_HEADER_INDEX, match->header_index) ||
+	    nla_put_u32(skb, DEVLINK_ATTR_DPIPE_HEADER_ID, header->id) ||
+	    nla_put_u32(skb, DEVLINK_ATTR_DPIPE_FIELD_ID, field->id) ||
+	    nla_put_u8(skb, DEVLINK_ATTR_DPIPE_HEADER_GLOBAL, header->global))
+		goto nla_put_failure;
+
+	nla_nest_end(skb, match_attr);
+	return 0;
+
+nla_put_failure:
+	nla_nest_cancel(skb, match_attr);
+	return -EMSGSIZE;
+}
+EXPORT_SYMBOL_GPL(devlink_dpipe_match_put);
+
+static int devlink_dpipe_matches_put(struct devlink_dpipe_table *table,
+				     struct sk_buff *skb)
+{
+	struct nlattr *matches_attr;
+
+	matches_attr = nla_nest_start(skb, DEVLINK_ATTR_DPIPE_TABLE_MATCHES);
+	if (!matches_attr)
+		return -EMSGSIZE;
+
+	if (table->table_ops->matches_dump(table->priv, skb))
+		goto nla_put_failure;
+
+	nla_nest_end(skb, matches_attr);
+	return 0;
+
+nla_put_failure:
+	nla_nest_cancel(skb, matches_attr);
+	return -EMSGSIZE;
+}
+
+int devlink_dpipe_action_put(struct sk_buff *skb,
+			     struct devlink_dpipe_action *action)
+{
+	struct devlink_dpipe_header *header = action->header;
+	struct devlink_dpipe_field *field = &header->fields[action->field_id];
+	struct nlattr *action_attr;
+
+	action_attr = nla_nest_start(skb, DEVLINK_ATTR_DPIPE_ACTION);
+	if (!action_attr)
+		return -EMSGSIZE;
+
+	if (nla_put_u32(skb, DEVLINK_ATTR_DPIPE_ACTION_TYPE, action->type) ||
+	    nla_put_u32(skb, DEVLINK_ATTR_DPIPE_HEADER_INDEX, action->header_index) ||
+	    nla_put_u32(skb, DEVLINK_ATTR_DPIPE_HEADER_ID, header->id) ||
+	    nla_put_u32(skb, DEVLINK_ATTR_DPIPE_FIELD_ID, field->id) ||
+	    nla_put_u8(skb, DEVLINK_ATTR_DPIPE_HEADER_GLOBAL, header->global))
+		goto nla_put_failure;
+
+	nla_nest_end(skb, action_attr);
+	return 0;
+
+nla_put_failure:
+	nla_nest_cancel(skb, action_attr);
+	return -EMSGSIZE;
+}
+EXPORT_SYMBOL_GPL(devlink_dpipe_action_put);
+
+static int devlink_dpipe_actions_put(struct devlink_dpipe_table *table,
+				     struct sk_buff *skb)
+{
+	struct nlattr *actions_attr;
+
+	actions_attr = nla_nest_start(skb, DEVLINK_ATTR_DPIPE_TABLE_ACTIONS);
+	if (!actions_attr)
+		return -EMSGSIZE;
+
+	if (table->table_ops->actions_dump(table->priv, skb))
+		goto nla_put_failure;
+
+	nla_nest_end(skb, actions_attr);
+	return 0;
+
+nla_put_failure:
+	nla_nest_cancel(skb, actions_attr);
+	return -EMSGSIZE;
+}
+
+static int devlink_dpipe_table_put(struct sk_buff *skb,
+				   struct devlink_dpipe_table *table)
+{
+	struct nlattr *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_u64_64bit(skb, DEVLINK_ATTR_DPIPE_TABLE_SIZE, table->size,
+			      DEVLINK_ATTR_PAD))
+		goto nla_put_failure;
+	if (nla_put_u8(skb, DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED,
+		       table->counters_enabled))
+		goto nla_put_failure;
+
+	if (devlink_dpipe_matches_put(table, skb))
+		goto nla_put_failure;
+
+	if (devlink_dpipe_actions_put(table, skb))
+		goto nla_put_failure;
+
+	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,
+				     const char *table_name)
+{
+	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) {
+		if (!table_name) {
+			err = devlink_dpipe_table_put(skb, table);
+			if (err) {
+				if (!i)
+					goto err_table_put;
+				incomplete = true;
+				break;
+			}
+		} else {
+			if (!strcmp(table->name, table_name)) {
+				err = devlink_dpipe_table_put(skb, table);
+				if (err)
+					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_table_get(struct sk_buff *skb,
+					  struct genl_info *info)
+{
+	struct devlink *devlink = info->user_ptr[0];
+	const char *table_name =  NULL;
+
+	if (info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME])
+		table_name = nla_data(info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME]);
+
+	return devlink_dpipe_tables_fill(info, DEVLINK_CMD_DPIPE_TABLE_GET, 0,
+					 &devlink->dpipe_table_list,
+					 table_name);
+}
+
+static int devlink_dpipe_value_put(struct sk_buff *skb,
+				   struct devlink_dpipe_value *value)
+{
+	if (nla_put(skb, DEVLINK_ATTR_DPIPE_VALUE,
+		    value->value_size, value->value))
+		return -EMSGSIZE;
+	if (value->mask)
+		if (nla_put(skb, DEVLINK_ATTR_DPIPE_VALUE_MASK,
+			    value->value_size, value->mask))
+			return -EMSGSIZE;
+	if (value->mapping_valid)
+		if (nla_put_u32(skb, DEVLINK_ATTR_DPIPE_VALUE_MAPPING,
+				value->mapping_value))
+			return -EMSGSIZE;
+	return 0;
+}
+
+static int devlink_dpipe_action_value_put(struct sk_buff *skb,
+					  struct devlink_dpipe_value *value)
+{
+	if (!value->action)
+		return -EINVAL;
+	if (devlink_dpipe_action_put(skb, value->action))
+		return -EMSGSIZE;
+	if (devlink_dpipe_value_put(skb, value))
+		return -EMSGSIZE;
+	return 0;
+}
+
+static int devlink_dpipe_action_values_put(struct sk_buff *skb,
+					   struct devlink_dpipe_value *values,
+					   unsigned int values_count)
+{
+	struct nlattr *action_attr;
+	int i;
+	int err;
+
+	for (i = 0; i < values_count; i++) {
+		action_attr = nla_nest_start(skb,
+					     DEVLINK_ATTR_DPIPE_ACTION_VALUE);
+		if (!action_attr)
+			return -EMSGSIZE;
+		err = devlink_dpipe_action_value_put(skb, &values[i]);
+		if (err)
+			goto err_action_value_put;
+		nla_nest_end(skb, action_attr);
+	}
+	return 0;
+
+err_action_value_put:
+	nla_nest_cancel(skb, action_attr);
+	return err;
+}
+
+static int devlink_dpipe_match_value_put(struct sk_buff *skb,
+					 struct devlink_dpipe_value *value)
+{
+	if (!value->match)
+		return -EINVAL;
+	if (devlink_dpipe_match_put(skb, value->match))
+		return -EMSGSIZE;
+	if (devlink_dpipe_value_put(skb, value))
+		return -EMSGSIZE;
+	return 0;
+}
+
+static int devlink_dpipe_match_values_put(struct sk_buff *skb,
+					  struct devlink_dpipe_value *values,
+					  unsigned int values_count)
+{
+	struct nlattr *match_attr;
+	int i;
+	int err;
+
+	for (i = 0; i < values_count; i++) {
+		match_attr = nla_nest_start(skb,
+					    DEVLINK_ATTR_DPIPE_MATCH_VALUE);
+		if (!match_attr)
+			return -EMSGSIZE;
+		err = devlink_dpipe_match_value_put(skb, &values[i]);
+		if (err)
+			goto err_match_value_put;
+		nla_nest_end(skb, match_attr);
+	}
+	return 0;
+
+err_match_value_put:
+	nla_nest_cancel(skb, match_attr);
+	return err;
+}
+
+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_u64_64bit(skb, DEVLINK_ATTR_DPIPE_ENTRY_INDEX, entry->index,
+			      DEVLINK_ATTR_PAD))
+		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_MATCH_VALUES);
+	if (!matches_attr)
+		goto nla_put_failure;
+
+	err = devlink_dpipe_match_values_put(skb, entry->match_values,
+					     entry->match_values_count);
+	if (err) {
+		nla_nest_cancel(skb, matches_attr);
+		goto err_match_values_put;
+	}
+	nla_nest_end(skb, matches_attr);
+
+	actions_attr = nla_nest_start(skb,
+				      DEVLINK_ATTR_DPIPE_ENTRY_ACTION_VALUES);
+	if (!actions_attr)
+		goto nla_put_failure;
+
+	err = devlink_dpipe_action_values_put(skb, entry->action_values,
+					      entry->action_values_count);
+	if (err) {
+		nla_nest_cancel(skb, actions_attr);
+		goto err_action_values_put;
+	}
+	nla_nest_end(skb, actions_attr);
 
+	nla_nest_end(skb, entry_attr);
 	return 0;
+
+nla_put_failure:
+	err = -EMSGSIZE;
+err_match_values_put:
+err_action_values_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_ctx_prepare(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;
+}
+EXPORT_SYMBOL_GPL(devlink_dpipe_entry_ctx_prepare);
+
+int devlink_dpipe_entry_ctx_append(struct devlink_dpipe_dump_ctx *dump_ctx,
+				   struct devlink_dpipe_entry *entry)
+{
+	return devlink_dpipe_entry_put(dump_ctx->skb, entry);
+}
+EXPORT_SYMBOL_GPL(devlink_dpipe_entry_ctx_append);
+
+int devlink_dpipe_entry_ctx_close(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;
+}
+EXPORT_SYMBOL_GPL(devlink_dpipe_entry_ctx_close);
+
+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) ||
+		    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) ||
+	    nla_put_u8(skb, DEVLINK_ATTR_DPIPE_HEADER_GLOBAL, header->global))
+		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];
+
+	if (!devlink->dpipe_headers)
+		return -EOPNOTSUPP;
+	return devlink_dpipe_headers_fill(info, DEVLINK_CMD_DPIPE_HEADERS_GET,
+					  0, devlink->dpipe_headers);
+}
+
+static int devlink_dpipe_table_counters_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->counters_set_update)
+		table->table_ops->counters_set_update(table->priv, enable);
+	return 0;
+}
+
+static int devlink_nl_cmd_dpipe_table_counters_set(struct sk_buff *skb,
+						   struct genl_info *info)
+{
+	struct devlink *devlink = info->user_ptr[0];
+	const char *table_name;
+	bool counters_enable;
+
+	if (!info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME] ||
+	    !info->attrs[DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED])
+		return -EINVAL;
+
+	table_name = nla_data(info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME]);
+	counters_enable = !!nla_get_u8(info->attrs[DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED]);
+
+	return devlink_dpipe_table_counters_set(devlink, table_name,
+						counters_enable);
 }
 
 static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {
@@ -1512,6 +2190,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_U8 },
 };
 
 static const struct genl_ops devlink_nl_ops[] = {
@@ -1644,6 +2324,34 @@ static const struct genl_ops devlink_nl_ops[] = {
 		.flags = GENL_ADMIN_PERM,
 		.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
 	},
+	{
+		.cmd = DEVLINK_CMD_DPIPE_TABLE_GET,
+		.doit = devlink_nl_cmd_dpipe_table_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_COUNTERS_SET,
+		.doit = devlink_nl_cmd_dpipe_table_counters_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 +2388,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 +2589,133 @@ 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
+ *	@size: size
+ *	@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, u64 size,
+				 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->size = size;
+	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);
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [patch net-next 2/8] mlxsw: reg: Add counter fields to RITR register
  2017-03-25  7:35 [patch net-next 0/8] Add support for pipeline debug (dpipe) Jiri Pirko
  2017-03-25  7:35 ` [patch net-next 1/8] devlink: Support " Jiri Pirko
@ 2017-03-25  7:35 ` Jiri Pirko
  2017-03-25  7:35 ` [patch net-next 3/8] mlxsw: spectrum: Add placeholder for dpipe Jiri Pirko
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: Jiri Pirko @ 2017-03-25  7:35 UTC (permalink / raw)
  To: netdev
  Cc: davem, arkadis, idosch, mlxsw, jhs, ivecera, roopa, f.fainelli,
	vivien.didelot, john.fastabend, andrew, simon.horman

From: Arkadi Sharshevsky <arkadis@mellanox.com>

Update RITR for counter support. This allows adding counters for
ASIC's router ports.

Signed-off-by: Arkadi Sharshevsky <arkadis@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
---
 drivers/net/ethernet/mellanox/mlxsw/reg.h | 54 +++++++++++++++++++++++++++++++
 1 file changed, 54 insertions(+)

diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h
index e7a652c..82aaa3e 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/reg.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h
@@ -4125,6 +4125,60 @@ MLXSW_ITEM32(reg, ritr, sp_if_system_port, 0x08, 0, 16);
  */
 MLXSW_ITEM32(reg, ritr, sp_if_vid, 0x18, 0, 12);
 
+/* Shared between ingress/egress */
+enum mlxsw_reg_ritr_counter_set_type {
+	/* No Count. */
+	MLXSW_REG_RITR_COUNTER_SET_TYPE_NO_COUNT = 0x0,
+	/* Basic. Used for router interfaces, counting the following:
+	 *	- Error and Discard counters.
+	 *	- Unicast, Multicast and Broadcast counters. Sharing the
+	 *	  same set of counters for the different type of traffic
+	 *	  (IPv4, IPv6 and mpls).
+	 */
+	MLXSW_REG_RITR_COUNTER_SET_TYPE_BASIC = 0x9,
+};
+
+/* reg_ritr_ingress_counter_index
+ * Counter Index for flow counter.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, ritr, ingress_counter_index, 0x38, 0, 24);
+
+/* reg_ritr_ingress_counter_set_type
+ * Igress Counter Set Type for router interface counter.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, ritr, ingress_counter_set_type, 0x38, 24, 8);
+
+/* reg_ritr_egress_counter_index
+ * Counter Index for flow counter.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, ritr, egress_counter_index, 0x3C, 0, 24);
+
+/* reg_ritr_egress_counter_set_type
+ * Egress Counter Set Type for router interface counter.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, ritr, egress_counter_set_type, 0x3C, 24, 8);
+
+static inline void mlxsw_reg_ritr_counter_pack(char *payload, u32 index,
+					       bool enable, bool egress)
+{
+	enum mlxsw_reg_ritr_counter_set_type set_type;
+
+	if (enable)
+		set_type = MLXSW_REG_RITR_COUNTER_SET_TYPE_BASIC;
+	else
+		set_type = MLXSW_REG_RITR_COUNTER_SET_TYPE_NO_COUNT;
+	mlxsw_reg_ritr_egress_counter_set_type_set(payload, set_type);
+
+	if (egress)
+		mlxsw_reg_ritr_egress_counter_index_set(payload, index);
+	else
+		mlxsw_reg_ritr_ingress_counter_index_set(payload, index);
+}
+
 static inline void mlxsw_reg_ritr_rif_pack(char *payload, u16 rif)
 {
 	MLXSW_REG_ZERO(ritr, payload);
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [patch net-next 3/8] mlxsw: spectrum: Add placeholder for dpipe
  2017-03-25  7:35 [patch net-next 0/8] Add support for pipeline debug (dpipe) Jiri Pirko
  2017-03-25  7:35 ` [patch net-next 1/8] devlink: Support " Jiri Pirko
  2017-03-25  7:35 ` [patch net-next 2/8] mlxsw: reg: Add counter fields to RITR register Jiri Pirko
@ 2017-03-25  7:35 ` Jiri Pirko
  2017-03-25  7:35 ` [patch net-next 4/8] mlxsw: spectrum: Add definition for egress rif table Jiri Pirko
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: Jiri Pirko @ 2017-03-25  7:35 UTC (permalink / raw)
  To: netdev
  Cc: davem, arkadis, idosch, mlxsw, jhs, ivecera, roopa, f.fainelli,
	vivien.didelot, john.fastabend, andrew, simon.horman

From: Arkadi Sharshevsky <arkadis@mellanox.com>

Add placeholder for dpipe. Support for specific tables and headers will
be introduced in following patches. The headers are shared between all
mlxsw_sp instances.

Signed-off-by: Arkadi Sharshevsky <arkadis@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
---
 drivers/net/ethernet/mellanox/mlxsw/Makefile       |  2 +-
 drivers/net/ethernet/mellanox/mlxsw/spectrum.c     | 10 ++++
 .../net/ethernet/mellanox/mlxsw/spectrum_dpipe.c   | 57 ++++++++++++++++++++++
 .../net/ethernet/mellanox/mlxsw/spectrum_dpipe.h   | 41 ++++++++++++++++
 4 files changed, 109 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c
 create mode 100644 drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h

diff --git a/drivers/net/ethernet/mellanox/mlxsw/Makefile b/drivers/net/ethernet/mellanox/mlxsw/Makefile
index 95fcacf..2fb8c65 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/Makefile
+++ b/drivers/net/ethernet/mellanox/mlxsw/Makefile
@@ -16,7 +16,7 @@ mlxsw_spectrum-objs		:= spectrum.o spectrum_buffers.o \
 				   spectrum_switchdev.o spectrum_router.o \
 				   spectrum_kvdl.o spectrum_acl_tcam.o \
 				   spectrum_acl.o spectrum_flower.o \
-				   spectrum_cnt.o
+				   spectrum_cnt.o spectrum_dpipe.o
 mlxsw_spectrum-$(CONFIG_MLXSW_SPECTRUM_DCB)	+= spectrum_dcb.o
 obj-$(CONFIG_MLXSW_MINIMAL)	+= mlxsw_minimal.o
 mlxsw_minimal-objs		:= minimal.o
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
index eabfe5c..b031f09 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
@@ -67,6 +67,7 @@
 #include "trap.h"
 #include "txheader.h"
 #include "spectrum_cnt.h"
+#include "spectrum_dpipe.h"
 
 static const char mlxsw_sp_driver_name[] = "mlxsw_spectrum";
 static const char mlxsw_sp_driver_version[] = "1.0";
@@ -3339,6 +3340,12 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core,
 		goto err_counter_pool_init;
 	}
 
+	err = mlxsw_sp_dpipe_init(mlxsw_sp);
+	if (err) {
+		dev_err(mlxsw_sp->bus_info->dev, "Failed to init pipeline debug\n");
+		goto err_dpipe_init;
+	}
+
 	err = mlxsw_sp_ports_create(mlxsw_sp);
 	if (err) {
 		dev_err(mlxsw_sp->bus_info->dev, "Failed to create ports\n");
@@ -3348,6 +3355,8 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core,
 	return 0;
 
 err_ports_create:
+	mlxsw_sp_dpipe_fini(mlxsw_sp);
+err_dpipe_init:
 	mlxsw_sp_counter_pool_fini(mlxsw_sp);
 err_counter_pool_init:
 	mlxsw_sp_acl_fini(mlxsw_sp);
@@ -3372,6 +3381,7 @@ static void mlxsw_sp_fini(struct mlxsw_core *mlxsw_core)
 	struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
 
 	mlxsw_sp_ports_remove(mlxsw_sp);
+	mlxsw_sp_dpipe_fini(mlxsw_sp);
 	mlxsw_sp_counter_pool_fini(mlxsw_sp);
 	mlxsw_sp_acl_fini(mlxsw_sp);
 	mlxsw_sp_span_fini(mlxsw_sp);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c
new file mode 100644
index 0000000..7c87bc8
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c
@@ -0,0 +1,57 @@
+/*
+ * drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c
+ * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2017 Arkadi Sharshevsky <arakdis@mellanox.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/kernel.h>
+#include <net/devlink.h>
+
+#include "spectrum.h"
+#include "spectrum_dpipe.h"
+
+static struct devlink_dpipe_header *mlxsw_dpipe_headers[] = {};
+
+static struct devlink_dpipe_headers mlxsw_sp_dpipe_headers = {
+	.headers = mlxsw_dpipe_headers,
+	.headers_count = ARRAY_SIZE(mlxsw_dpipe_headers),
+};
+
+int mlxsw_sp_dpipe_init(struct mlxsw_sp *mlxsw_sp)
+{
+	return devlink_dpipe_headers_register(priv_to_devlink(mlxsw_sp->core),
+					      &mlxsw_sp_dpipe_headers);
+}
+
+void mlxsw_sp_dpipe_fini(struct mlxsw_sp *mlxsw_sp)
+{
+	devlink_dpipe_headers_unregister(priv_to_devlink(mlxsw_sp->core));
+}
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h
new file mode 100644
index 0000000..ad16259
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h
@@ -0,0 +1,41 @@
+/*
+ * drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h
+ * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2017 Arkadi Sharshevsky <arkadis@mellanox.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _MLXSW_PIPELINE_H_
+#define _MLXSW_PIPELINE_H_
+
+int mlxsw_sp_dpipe_init(struct mlxsw_sp *mlxsw_sp);
+void mlxsw_sp_dpipe_fini(struct mlxsw_sp *mlxsw_sp);
+
+#endif /* _MLXSW_PIPELINE_H_*/
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [patch net-next 4/8] mlxsw: spectrum: Add definition for egress rif table
  2017-03-25  7:35 [patch net-next 0/8] Add support for pipeline debug (dpipe) Jiri Pirko
                   ` (2 preceding siblings ...)
  2017-03-25  7:35 ` [patch net-next 3/8] mlxsw: spectrum: Add placeholder for dpipe Jiri Pirko
@ 2017-03-25  7:35 ` Jiri Pirko
  2017-03-25  7:35 ` [patch net-next 5/8] mlxsw: reg: Add Router Interface Counter Register Jiri Pirko
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: Jiri Pirko @ 2017-03-25  7:35 UTC (permalink / raw)
  To: netdev
  Cc: davem, arkadis, idosch, mlxsw, jhs, ivecera, roopa, f.fainelli,
	vivien.didelot, john.fastabend, andrew, simon.horman

From: Arkadi Sharshevsky <arkadis@mellanox.com>

Add definition for egress router interface table. This table describes
the final part in the routing pipeline. This table matches the egress
interface index (rif index, which is set by the previous stages and
determine the out port) and makes the decision of forwarding the packet
towards the L2 logic or dropping it.

The metadata header is added to represent this internal information.
The rif index field is mapped logically to netdevice ifindex.

Signed-off-by: Arkadi Sharshevsky <arkadis@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
---
 .../net/ethernet/mellanox/mlxsw/spectrum_dpipe.c   | 117 ++++++++++++++++++++-
 .../net/ethernet/mellanox/mlxsw/spectrum_dpipe.h   |   2 +
 2 files changed, 115 insertions(+), 4 deletions(-)

diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c
index 7c87bc8..60f2207 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c
@@ -38,20 +38,129 @@
 #include "spectrum.h"
 #include "spectrum_dpipe.h"
 
-static struct devlink_dpipe_header *mlxsw_dpipe_headers[] = {};
+enum mlxsw_sp_field_metadata_id {
+	MLXSW_SP_DPIPE_FIELD_METADATA_ERIF_PORT,
+	MLXSW_SP_DPIPE_FIELD_METADATA_L3_FORWARD,
+	MLXSW_SP_DPIPE_FIELD_METADATA_L3_DROP,
+};
+
+static struct devlink_dpipe_field mlxsw_sp_dpipe_fields_metadata[] = {
+	{ .name = "erif_port",
+	  .id = MLXSW_SP_DPIPE_FIELD_METADATA_ERIF_PORT,
+	  .bitwidth = 32,
+	  .mapping_type = DEVLINK_DPIPE_FIELD_MAPPING_TYPE_IFINDEX,
+	},
+	{ .name = "l3_forward",
+	  .id = MLXSW_SP_DPIPE_FIELD_METADATA_L3_FORWARD,
+	  .bitwidth = 1,
+	},
+	{ .name = "l2_drop",
+	  .id = MLXSW_SP_DPIPE_FIELD_METADATA_L3_DROP,
+	  .bitwidth = 1,
+	},
+};
+
+enum mlxsw_sp_dpipe_header_id {
+	MLXSW_SP_DPIPE_HEADER_METADATA,
+};
+
+static struct devlink_dpipe_header mlxsw_sp_dpipe_header_metadata = {
+	.name = "mlxsw_meta",
+	.id = MLXSW_SP_DPIPE_HEADER_METADATA,
+	.fields = mlxsw_sp_dpipe_fields_metadata,
+	.fields_count = ARRAY_SIZE(mlxsw_sp_dpipe_fields_metadata),
+};
+
+static struct devlink_dpipe_header *mlxsw_dpipe_headers[] = {
+	&mlxsw_sp_dpipe_header_metadata,
+};
 
 static struct devlink_dpipe_headers mlxsw_sp_dpipe_headers = {
 	.headers = mlxsw_dpipe_headers,
 	.headers_count = ARRAY_SIZE(mlxsw_dpipe_headers),
 };
 
+static int mlxsw_sp_dpipe_table_erif_actions_dump(void *priv,
+						  struct sk_buff *skb)
+{
+	struct devlink_dpipe_action action = {0};
+	int err;
+
+	action.type = DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY;
+	action.header = &mlxsw_sp_dpipe_header_metadata;
+	action.field_id = MLXSW_SP_DPIPE_FIELD_METADATA_L3_FORWARD;
+
+	err = devlink_dpipe_action_put(skb, &action);
+	if (err)
+		return err;
+
+	action.type = DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY;
+	action.header = &mlxsw_sp_dpipe_header_metadata;
+	action.field_id = MLXSW_SP_DPIPE_FIELD_METADATA_L3_DROP;
+
+	return devlink_dpipe_action_put(skb, &action);
+}
+
+static int mlxsw_sp_dpipe_table_erif_matches_dump(void *priv,
+						  struct sk_buff *skb)
+{
+	struct devlink_dpipe_match match = {0};
+
+	match.type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT;
+	match.header = &mlxsw_sp_dpipe_header_metadata;
+	match.field_id = MLXSW_SP_DPIPE_FIELD_METADATA_ERIF_PORT;
+
+	return devlink_dpipe_match_put(skb, &match);
+}
+
+static struct devlink_dpipe_table_ops mlxsw_sp_erif_ops = {
+	.matches_dump = mlxsw_sp_dpipe_table_erif_matches_dump,
+	.actions_dump = mlxsw_sp_dpipe_table_erif_actions_dump,
+};
+
+static int mlxsw_sp_dpipe_erif_table_init(struct mlxsw_sp *mlxsw_sp)
+{
+	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
+	u64 table_size;
+
+	table_size = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
+	return devlink_dpipe_table_register(devlink,
+					    MLXSW_SP_DPIPE_TABLE_NAME_ERIF,
+					    &mlxsw_sp_erif_ops,
+					    mlxsw_sp, table_size,
+					    false);
+}
+
+static void mlxsw_sp_dpipe_erif_table_fini(struct mlxsw_sp *mlxsw_sp)
+{
+	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
+
+	devlink_dpipe_table_unregister(devlink, MLXSW_SP_DPIPE_TABLE_NAME_ERIF);
+}
+
 int mlxsw_sp_dpipe_init(struct mlxsw_sp *mlxsw_sp)
 {
-	return devlink_dpipe_headers_register(priv_to_devlink(mlxsw_sp->core),
-					      &mlxsw_sp_dpipe_headers);
+	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
+	int err;
+
+	err = devlink_dpipe_headers_register(devlink,
+					     &mlxsw_sp_dpipe_headers);
+	if (err)
+		return err;
+	err = mlxsw_sp_dpipe_erif_table_init(mlxsw_sp);
+	if (err)
+		goto err_erif_register;
+	return 0;
+
+err_erif_register:
+	devlink_dpipe_headers_unregister(priv_to_devlink(mlxsw_sp->core));
+	return err;
 }
 
 void mlxsw_sp_dpipe_fini(struct mlxsw_sp *mlxsw_sp)
 {
-	devlink_dpipe_headers_unregister(priv_to_devlink(mlxsw_sp->core));
+	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
+
+	mlxsw_sp_dpipe_erif_table_fini(mlxsw_sp);
+	devlink_dpipe_headers_unregister(devlink);
 }
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h
index ad16259..d208929 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h
@@ -38,4 +38,6 @@
 int mlxsw_sp_dpipe_init(struct mlxsw_sp *mlxsw_sp);
 void mlxsw_sp_dpipe_fini(struct mlxsw_sp *mlxsw_sp);
 
+#define MLXSW_SP_DPIPE_TABLE_NAME_ERIF "mlxsw_erif"
+
 #endif /* _MLXSW_PIPELINE_H_*/
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [patch net-next 5/8] mlxsw: reg: Add Router Interface Counter Register
  2017-03-25  7:35 [patch net-next 0/8] Add support for pipeline debug (dpipe) Jiri Pirko
                   ` (3 preceding siblings ...)
  2017-03-25  7:35 ` [patch net-next 4/8] mlxsw: spectrum: Add definition for egress rif table Jiri Pirko
@ 2017-03-25  7:35 ` Jiri Pirko
  2017-03-25  7:35 ` [patch net-next 6/8] mlxsw: spectrum: Support for counters on router interfaces Jiri Pirko
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: Jiri Pirko @ 2017-03-25  7:35 UTC (permalink / raw)
  To: netdev
  Cc: davem, arkadis, idosch, mlxsw, jhs, ivecera, roopa, f.fainelli,
	vivien.didelot, john.fastabend, andrew, simon.horman

From: Arkadi Sharshevsky <arkadis@mellanox.com>

The RICNT register retrieves per port performance counter. It will be
used to query the router interfaces statistics.

Signed-off-by: Arkadi Sharshevsky <arkadis@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
---
 drivers/net/ethernet/mellanox/mlxsw/reg.h | 124 ++++++++++++++++++++++++++++++
 1 file changed, 124 insertions(+)

diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h
index 82aaa3e..83b277c 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/reg.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h
@@ -4341,6 +4341,129 @@ static inline void mlxsw_reg_ratr_eth_entry_pack(char *payload,
 	mlxsw_reg_ratr_eth_destination_mac_memcpy_to(payload, dest_mac);
 }
 
+/* RICNT - Router Interface Counter Register
+ * -----------------------------------------
+ * The RICNT register retrieves per port performance counters
+ */
+#define MLXSW_REG_RICNT_ID 0x800B
+#define MLXSW_REG_RICNT_LEN 0x100
+
+MLXSW_REG_DEFINE(ricnt, MLXSW_REG_RICNT_ID, MLXSW_REG_RICNT_LEN);
+
+/* reg_ricnt_counter_index
+ * Counter index
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, ricnt, counter_index, 0x04, 0, 24);
+
+enum mlxsw_reg_ricnt_counter_set_type {
+	/* No Count. */
+	MLXSW_REG_RICNT_COUNTER_SET_TYPE_NO_COUNT = 0x00,
+	/* Basic. Used for router interfaces, counting the following:
+	 *	- Error and Discard counters.
+	 *	- Unicast, Multicast and Broadcast counters. Sharing the
+	 *	  same set of counters for the different type of traffic
+	 *	  (IPv4, IPv6 and mpls).
+	 */
+	MLXSW_REG_RICNT_COUNTER_SET_TYPE_BASIC = 0x09,
+};
+
+/* reg_ricnt_counter_set_type
+ * Counter Set Type for router interface counter
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, ricnt, counter_set_type, 0x04, 24, 8);
+
+enum mlxsw_reg_ricnt_opcode {
+	/* Nop. Supported only for read access*/
+	MLXSW_REG_RICNT_OPCODE_NOP = 0x00,
+	/* Clear. Setting the clr bit will reset the counter value for
+	 * all counters of the specified Router Interface.
+	 */
+	MLXSW_REG_RICNT_OPCODE_CLEAR = 0x08,
+};
+
+/* reg_ricnt_opcode
+ * Opcode
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, ricnt, op, 0x00, 28, 4);
+
+/* reg_ricnt_good_unicast_packets
+ * good unicast packets.
+ * Access: RW
+ */
+MLXSW_ITEM64(reg, ricnt, good_unicast_packets, 0x08, 0, 64);
+
+/* reg_ricnt_good_multicast_packets
+ * good multicast packets.
+ * Access: RW
+ */
+MLXSW_ITEM64(reg, ricnt, good_multicast_packets, 0x10, 0, 64);
+
+/* reg_ricnt_good_broadcast_packets
+ * good broadcast packets
+ * Access: RW
+ */
+MLXSW_ITEM64(reg, ricnt, good_broadcast_packets, 0x18, 0, 64);
+
+/* reg_ricnt_good_unicast_bytes
+ * A count of L3 data and padding octets not including L2 headers
+ * for good unicast frames.
+ * Access: RW
+ */
+MLXSW_ITEM64(reg, ricnt, good_unicast_bytes, 0x20, 0, 64);
+
+/* reg_ricnt_good_multicast_bytes
+ * A count of L3 data and padding octets not including L2 headers
+ * for good multicast frames.
+ * Access: RW
+ */
+MLXSW_ITEM64(reg, ricnt, good_multicast_bytes, 0x28, 0, 64);
+
+/* reg_ritr_good_broadcast_bytes
+ * A count of L3 data and padding octets not including L2 headers
+ * for good broadcast frames.
+ * Access: RW
+ */
+MLXSW_ITEM64(reg, ricnt, good_broadcast_bytes, 0x30, 0, 64);
+
+/* reg_ricnt_error_packets
+ * A count of errored frames that do not pass the router checks.
+ * Access: RW
+ */
+MLXSW_ITEM64(reg, ricnt, error_packets, 0x38, 0, 64);
+
+/* reg_ricnt_discrad_packets
+ * A count of non-errored frames that do not pass the router checks.
+ * Access: RW
+ */
+MLXSW_ITEM64(reg, ricnt, discard_packets, 0x40, 0, 64);
+
+/* reg_ricnt_error_bytes
+ * A count of L3 data and padding octets not including L2 headers
+ * for errored frames.
+ * Access: RW
+ */
+MLXSW_ITEM64(reg, ricnt, error_bytes, 0x48, 0, 64);
+
+/* reg_ricnt_discard_bytes
+ * A count of L3 data and padding octets not including L2 headers
+ * for non-errored frames that do not pass the router checks.
+ * Access: RW
+ */
+MLXSW_ITEM64(reg, ricnt, discard_bytes, 0x50, 0, 64);
+
+static inline void mlxsw_reg_ricnt_pack(char *payload, u32 index,
+					enum mlxsw_reg_ricnt_opcode op)
+{
+	MLXSW_REG_ZERO(ricnt, payload);
+	mlxsw_reg_ricnt_op_set(payload, op);
+	mlxsw_reg_ricnt_counter_index_set(payload, index);
+	mlxsw_reg_ricnt_counter_set_type_set(payload,
+					     MLXSW_REG_RICNT_COUNTER_SET_TYPE_BASIC);
+}
+
 /* RALTA - Router Algorithmic LPM Tree Allocation Register
  * -------------------------------------------------------
  * RALTA is used to allocate the LPM trees of the SHSPM method.
@@ -6080,6 +6203,7 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = {
 	MLXSW_REG(rgcr),
 	MLXSW_REG(ritr),
 	MLXSW_REG(ratr),
+	MLXSW_REG(ricnt),
 	MLXSW_REG(ralta),
 	MLXSW_REG(ralst),
 	MLXSW_REG(raltb),
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [patch net-next 6/8] mlxsw: spectrum: Support for counters on router interfaces
  2017-03-25  7:35 [patch net-next 0/8] Add support for pipeline debug (dpipe) Jiri Pirko
                   ` (4 preceding siblings ...)
  2017-03-25  7:35 ` [patch net-next 5/8] mlxsw: reg: Add Router Interface Counter Register Jiri Pirko
@ 2017-03-25  7:35 ` Jiri Pirko
  2017-03-25  7:35 ` [patch net-next 7/8] mlxsw: spectrum_router: Add rif helper functions Jiri Pirko
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: Jiri Pirko @ 2017-03-25  7:35 UTC (permalink / raw)
  To: netdev
  Cc: davem, arkadis, idosch, mlxsw, jhs, ivecera, roopa, f.fainelli,
	vivien.didelot, john.fastabend, andrew, simon.horman

From: Arkadi Sharshevsky <arkadis@mellanox.com>

Add support for counter allocation on router interfaces. The allocation
depends on the counter state of relevant table. In case the counting is
disabled or no counters left the counter index will be set as invalid.

Also a counter pool for router allocation is added.

Signed-off-by: Arakdi Sharshevsky <arkadis@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
---
 drivers/net/ethernet/mellanox/mlxsw/resources.h    |   2 +
 drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c |   9 ++
 drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.h |   1 +
 .../net/ethernet/mellanox/mlxsw/spectrum_router.c  | 160 +++++++++++++++++++++
 .../net/ethernet/mellanox/mlxsw/spectrum_router.h  |  56 ++++++++
 5 files changed, 228 insertions(+)
 create mode 100644 drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h

diff --git a/drivers/net/ethernet/mellanox/mlxsw/resources.h b/drivers/net/ethernet/mellanox/mlxsw/resources.h
index ee09769..9556d93 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/resources.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/resources.h
@@ -46,6 +46,7 @@ enum mlxsw_res_id {
 	MLXSW_RES_ID_COUNTER_POOL_SIZE,
 	MLXSW_RES_ID_MAX_SPAN,
 	MLXSW_RES_ID_COUNTER_SIZE_PACKETS_BYTES,
+	MLXSW_RES_ID_COUNTER_SIZE_ROUTER_BASIC,
 	MLXSW_RES_ID_MAX_SYSTEM_PORT,
 	MLXSW_RES_ID_MAX_LAG,
 	MLXSW_RES_ID_MAX_LAG_MEMBERS,
@@ -82,6 +83,7 @@ static u16 mlxsw_res_ids[] = {
 	[MLXSW_RES_ID_COUNTER_POOL_SIZE] = 0x2410,
 	[MLXSW_RES_ID_MAX_SPAN] = 0x2420,
 	[MLXSW_RES_ID_COUNTER_SIZE_PACKETS_BYTES] = 0x2443,
+	[MLXSW_RES_ID_COUNTER_SIZE_ROUTER_BASIC] = 0x2449,
 	[MLXSW_RES_ID_MAX_SYSTEM_PORT] = 0x2502,
 	[MLXSW_RES_ID_MAX_LAG] = 0x2520,
 	[MLXSW_RES_ID_MAX_LAG_MEMBERS] = 0x2521,
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c
index 1631e01..0f46775 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c
@@ -56,6 +56,9 @@ static struct mlxsw_sp_counter_sub_pool mlxsw_sp_counter_sub_pools[] = {
 	[MLXSW_SP_COUNTER_SUB_POOL_FLOW] = {
 		.bank_count = 6,
 	},
+	[MLXSW_SP_COUNTER_SUB_POOL_RIF] = {
+		.bank_count = 2,
+	}
 };
 
 static int mlxsw_sp_counter_pool_validate(struct mlxsw_sp *mlxsw_sp)
@@ -83,6 +86,12 @@ static int mlxsw_sp_counter_sub_pools_prepare(struct mlxsw_sp *mlxsw_sp)
 		return -EIO;
 	sub_pool->entry_size = MLXSW_CORE_RES_GET(mlxsw_sp->core,
 						  COUNTER_SIZE_PACKETS_BYTES);
+	/* Prepare erif pool*/
+	sub_pool = &mlxsw_sp_counter_sub_pools[MLXSW_SP_COUNTER_SUB_POOL_RIF];
+	if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, COUNTER_SIZE_ROUTER_BASIC))
+		return -EIO;
+	sub_pool->entry_size = MLXSW_CORE_RES_GET(mlxsw_sp->core,
+						  COUNTER_SIZE_ROUTER_BASIC);
 	return 0;
 }
 
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.h
index 031bc4a..fd34d0a 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.h
@@ -39,6 +39,7 @@
 
 enum mlxsw_sp_counter_sub_pool_id {
 	MLXSW_SP_COUNTER_SUB_POOL_FLOW,
+	MLXSW_SP_COUNTER_SUB_POOL_RIF,
 };
 
 int mlxsw_sp_counter_alloc(struct mlxsw_sp *mlxsw_sp,
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
index 905d459..5df234d 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
@@ -52,6 +52,9 @@
 #include "spectrum.h"
 #include "core.h"
 #include "reg.h"
+#include "spectrum_cnt.h"
+#include "spectrum_dpipe.h"
+#include "spectrum_router.h"
 
 struct mlxsw_sp_rif {
 	struct list_head nexthop_list;
@@ -62,8 +65,153 @@ struct mlxsw_sp_rif {
 	int mtu;
 	u16 rif_index;
 	u16 vr_id;
+	unsigned int counter_ingress;
+	bool counter_ingress_valid;
+	unsigned int counter_egress;
+	bool counter_egress_valid;
 };
 
+static void
+mlxsw_sp_rif_p_counter_get(struct mlxsw_sp_rif *rif,
+			   enum mlxsw_sp_rif_counter_dir dir,
+			   unsigned int **pp_counter_index)
+{
+	switch (dir) {
+	case MLXSW_SP_RIF_COUNTER_EGRESS:
+		*pp_counter_index = &rif->counter_egress;
+		break;
+	case MLXSW_SP_RIF_COUNTER_INGRESS:
+		*pp_counter_index = &rif->counter_ingress;
+		break;
+	}
+}
+
+static bool
+mlxsw_sp_rif_counter_valid_get(struct mlxsw_sp_rif *rif,
+			       enum mlxsw_sp_rif_counter_dir dir)
+{
+	switch (dir) {
+	case MLXSW_SP_RIF_COUNTER_EGRESS:
+		return rif->counter_egress_valid;
+	case MLXSW_SP_RIF_COUNTER_INGRESS:
+		return rif->counter_ingress_valid;
+	}
+	return false;
+}
+
+static void
+mlxsw_sp_rif_counter_valid_set(struct mlxsw_sp_rif *rif,
+			       enum mlxsw_sp_rif_counter_dir dir,
+			       bool valid)
+{
+	switch (dir) {
+	case MLXSW_SP_RIF_COUNTER_EGRESS:
+		rif->counter_egress_valid = valid;
+		break;
+	case MLXSW_SP_RIF_COUNTER_INGRESS:
+		rif->counter_ingress_valid = valid;
+		break;
+	}
+}
+
+static int mlxsw_sp_rif_counter_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
+				     unsigned int counter_index, bool enable,
+				     enum mlxsw_sp_rif_counter_dir dir)
+{
+	char ritr_pl[MLXSW_REG_RITR_LEN];
+	bool is_egress = false;
+	int err;
+
+	if (dir == MLXSW_SP_RIF_COUNTER_EGRESS)
+		is_egress = true;
+	mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index);
+	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
+	if (err)
+		return err;
+
+	mlxsw_reg_ritr_counter_pack(ritr_pl, counter_index, enable,
+				    is_egress);
+	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
+}
+
+int mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp,
+				   struct mlxsw_sp_rif *rif,
+				   enum mlxsw_sp_rif_counter_dir dir, u64 *cnt)
+{
+	unsigned int *p_counter_index = NULL;
+	char ricnt_pl[MLXSW_REG_RICNT_LEN];
+	bool valid;
+	int err;
+
+	valid = mlxsw_sp_rif_counter_valid_get(rif, dir);
+	if (!valid)
+		return -EINVAL;
+
+	mlxsw_sp_rif_p_counter_get(rif, dir, &p_counter_index);
+	mlxsw_reg_ricnt_pack(ricnt_pl, *p_counter_index,
+			     MLXSW_REG_RICNT_OPCODE_NOP);
+	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
+	if (err)
+		return err;
+	*cnt = mlxsw_reg_ricnt_good_unicast_packets_get(ricnt_pl);
+	return 0;
+}
+
+static int mlxsw_sp_rif_counter_clear(struct mlxsw_sp *mlxsw_sp,
+				      unsigned int counter_index)
+{
+	char ricnt_pl[MLXSW_REG_RICNT_LEN];
+
+	mlxsw_reg_ricnt_pack(ricnt_pl, counter_index,
+			     MLXSW_REG_RICNT_OPCODE_CLEAR);
+	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl);
+}
+
+int mlxsw_sp_rif_counter_alloc(struct mlxsw_sp *mlxsw_sp,
+			       struct mlxsw_sp_rif *rif,
+			       enum mlxsw_sp_rif_counter_dir dir)
+{
+	unsigned int *p_counter_index = NULL;
+	int err;
+
+	mlxsw_sp_rif_p_counter_get(rif, dir, &p_counter_index);
+	err = mlxsw_sp_counter_alloc(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
+				     p_counter_index);
+	if (err)
+		return err;
+
+	err = mlxsw_sp_rif_counter_clear(mlxsw_sp, *p_counter_index);
+	if (err)
+		goto err_counter_clear;
+
+	err = mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
+					*p_counter_index, true, dir);
+	if (err)
+		goto err_counter_edit;
+	mlxsw_sp_rif_counter_valid_set(rif, dir, true);
+	return 0;
+
+err_counter_edit:
+err_counter_clear:
+	mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
+			      *p_counter_index);
+	return err;
+}
+
+void mlxsw_sp_rif_counter_free(struct mlxsw_sp *mlxsw_sp,
+			       struct mlxsw_sp_rif *rif,
+			       enum mlxsw_sp_rif_counter_dir dir)
+{
+	unsigned int *p_counter_index;
+
+	mlxsw_sp_rif_p_counter_get(rif, dir, &p_counter_index);
+	mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
+				  *p_counter_index, false, dir);
+	mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF,
+			      *p_counter_index);
+	mlxsw_sp_rif_counter_valid_set(rif, dir, false);
+}
+
 static struct mlxsw_sp_rif *
 mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
 			 const struct net_device *dev);
@@ -2822,6 +2970,15 @@ mlxsw_sp_vport_rif_sp_create(struct mlxsw_sp_port *mlxsw_sp_vport,
 		goto err_rif_alloc;
 	}
 
+	if (devlink_dpipe_table_counter_enabled(priv_to_devlink(mlxsw_sp->core),
+						MLXSW_SP_DPIPE_TABLE_NAME_ERIF)) {
+		err = mlxsw_sp_rif_counter_alloc(mlxsw_sp, rif,
+						 MLXSW_SP_RIF_COUNTER_EGRESS);
+		if (err)
+			netdev_dbg(mlxsw_sp_vport->dev,
+				   "Counter alloc Failed err=%d\n", err);
+	}
+
 	f->rif = rif;
 	mlxsw_sp->rifs[rif_index] = rif;
 	vr->rif_count++;
@@ -2852,6 +3009,9 @@ static void mlxsw_sp_vport_rif_sp_destroy(struct mlxsw_sp_port *mlxsw_sp_vport,
 
 	mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif);
 
+	mlxsw_sp_rif_counter_free(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS);
+	mlxsw_sp_rif_counter_free(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_INGRESS);
+
 	vr->rif_count--;
 	mlxsw_sp->rifs[rif_index] = NULL;
 	f->rif = NULL;
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h
new file mode 100644
index 0000000..f469dc9
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h
@@ -0,0 +1,56 @@
+/*
+ * drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h
+ * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2017 Arkadi Sharshevsky <arkadis@mellanox.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _MLXSW_ROUTER_H_
+#define _MLXSW_ROUTER_H_
+
+#include "spectrum.h"
+
+enum mlxsw_sp_rif_counter_dir {
+	MLXSW_SP_RIF_COUNTER_INGRESS,
+	MLXSW_SP_RIF_COUNTER_EGRESS,
+};
+
+int mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp,
+				   struct mlxsw_sp_rif *rif,
+				   enum mlxsw_sp_rif_counter_dir dir,
+				   u64 *cnt);
+void mlxsw_sp_rif_counter_free(struct mlxsw_sp *mlxsw_sp,
+			       struct mlxsw_sp_rif *rif,
+			       enum mlxsw_sp_rif_counter_dir dir);
+int mlxsw_sp_rif_counter_alloc(struct mlxsw_sp *mlxsw_sp,
+			       struct mlxsw_sp_rif *rif,
+			       enum mlxsw_sp_rif_counter_dir dir);
+
+#endif /* _MLXSW_ROUTER_H_*/
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [patch net-next 7/8] mlxsw: spectrum_router: Add rif helper functions
  2017-03-25  7:35 [patch net-next 0/8] Add support for pipeline debug (dpipe) Jiri Pirko
                   ` (5 preceding siblings ...)
  2017-03-25  7:35 ` [patch net-next 6/8] mlxsw: spectrum: Support for counters on router interfaces Jiri Pirko
@ 2017-03-25  7:35 ` Jiri Pirko
  2017-03-25  7:35 ` [patch net-next 8/8] mlxsw: spectrum: Add Support for erif table entries access Jiri Pirko
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: Jiri Pirko @ 2017-03-25  7:35 UTC (permalink / raw)
  To: netdev
  Cc: davem, arkadis, idosch, mlxsw, jhs, ivecera, roopa, f.fainelli,
	vivien.didelot, john.fastabend, andrew, simon.horman

From: Arkadi Sharshevsky <arkadis@mellanox.com>

Add rif helper function to access the rif index and rif devices ifindex.
This functions will be used by dpipe in order to dump the rif table.

Signed-off-by: Arkadi Sharshevsky <arkadis@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
---
 drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 10 ++++++++++
 drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h |  2 ++
 2 files changed, 12 insertions(+)

diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
index 5df234d..7caf04b 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
@@ -2928,6 +2928,16 @@ mlxsw_sp_rif_alloc(u16 rif_index, u16 vr_id, struct net_device *l3_dev,
 	return rif;
 }
 
+u16 mlxsw_sp_rif_index_get(struct mlxsw_sp_rif *rif)
+{
+	return rif->rif_index;
+}
+
+int mlxsw_sp_rif_dev_ifindex_get(struct mlxsw_sp_rif *rif)
+{
+	return rif->dev->ifindex;
+}
+
 static struct mlxsw_sp_rif *
 mlxsw_sp_vport_rif_sp_create(struct mlxsw_sp_port *mlxsw_sp_vport,
 			     struct net_device *l3_dev)
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h
index f469dc9..dd0d183 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h
@@ -42,6 +42,8 @@ enum mlxsw_sp_rif_counter_dir {
 	MLXSW_SP_RIF_COUNTER_EGRESS,
 };
 
+u16 mlxsw_sp_rif_index_get(struct mlxsw_sp_rif *rif);
+int mlxsw_sp_rif_dev_ifindex_get(struct mlxsw_sp_rif *rif);
 int mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp,
 				   struct mlxsw_sp_rif *rif,
 				   enum mlxsw_sp_rif_counter_dir dir,
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [patch net-next 8/8] mlxsw: spectrum: Add Support for erif table entries access
  2017-03-25  7:35 [patch net-next 0/8] Add support for pipeline debug (dpipe) Jiri Pirko
                   ` (6 preceding siblings ...)
  2017-03-25  7:35 ` [patch net-next 7/8] mlxsw: spectrum_router: Add rif helper functions Jiri Pirko
@ 2017-03-25  7:35 ` Jiri Pirko
  2017-03-25  7:37 ` [patch iproute2/net-next] devlink: Add support for pipeline debug (dpipe) Jiri Pirko
  2017-03-27 22:48 ` [patch net-next 0/8] " David Miller
  9 siblings, 0 replies; 20+ messages in thread
From: Jiri Pirko @ 2017-03-25  7:35 UTC (permalink / raw)
  To: netdev
  Cc: davem, arkadis, idosch, mlxsw, jhs, ivecera, roopa, f.fainelli,
	vivien.didelot, john.fastabend, andrew, simon.horman

From: Arkadi Sharshevsky <arkadis@mellanox.com>

Implement dpipe's table ops for erif table which provide:
1. Getting the entries in the table with the associate values.
	- match on "mlxsw_meta:erif_index"
	- action on "mlxsw_meta:forwared_out"
2. Synchronize the hardware in case of enabling/disabling counters which
   mean removing erif counters from all interfaces.

Signed-off-by: Arkadi Sharshevsky <arkadis@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
---
 .../net/ethernet/mellanox/mlxsw/spectrum_dpipe.c   | 185 +++++++++++++++++++++
 1 file changed, 185 insertions(+)

diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c
index 60f2207..7ab759e 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c
@@ -37,6 +37,7 @@
 
 #include "spectrum.h"
 #include "spectrum_dpipe.h"
+#include "spectrum_router.h"
 
 enum mlxsw_sp_field_metadata_id {
 	MLXSW_SP_DPIPE_FIELD_METADATA_ERIF_PORT,
@@ -113,9 +114,193 @@ static int mlxsw_sp_dpipe_table_erif_matches_dump(void *priv,
 	return devlink_dpipe_match_put(skb, &match);
 }
 
+static void mlxsw_sp_erif_entry_clear(struct devlink_dpipe_entry *entry)
+{
+	unsigned int value_count, value_index;
+	struct devlink_dpipe_value *value;
+
+	value = entry->action_values;
+	value_count = entry->action_values_count;
+	for (value_index = 0; value_index < value_count; value_index++) {
+		kfree(value[value_index].value);
+		kfree(value[value_index].mask);
+	}
+
+	value = entry->match_values;
+	value_count = entry->match_values_count;
+	for (value_index = 0; value_index < value_count; value_index++) {
+		kfree(value[value_index].value);
+		kfree(value[value_index].mask);
+	}
+}
+
+static void
+mlxsw_sp_erif_match_action_prepare(struct devlink_dpipe_match *match,
+				   struct devlink_dpipe_action *action)
+{
+	action->type = DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY;
+	action->header = &mlxsw_sp_dpipe_header_metadata;
+	action->field_id = MLXSW_SP_DPIPE_FIELD_METADATA_L3_FORWARD;
+
+	match->type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT;
+	match->header = &mlxsw_sp_dpipe_header_metadata;
+	match->field_id = MLXSW_SP_DPIPE_FIELD_METADATA_ERIF_PORT;
+}
+
+static int mlxsw_sp_erif_entry_prepare(struct devlink_dpipe_entry *entry,
+				       struct devlink_dpipe_value *match_value,
+				       struct devlink_dpipe_match *match,
+				       struct devlink_dpipe_value *action_value,
+				       struct devlink_dpipe_action *action)
+{
+	entry->match_values = match_value;
+	entry->match_values_count = 1;
+
+	entry->action_values = action_value;
+	entry->action_values_count = 1;
+
+	match_value->match = match;
+	match_value->value_size = sizeof(u32);
+	match_value->value = kmalloc(match_value->value_size, GFP_KERNEL);
+	if (!match_value->value)
+		return -ENOMEM;
+
+	action_value->action = action;
+	action_value->value_size = sizeof(u32);
+	action_value->value = kmalloc(action_value->value_size, GFP_KERNEL);
+	if (!action_value->value)
+		goto err_action_alloc;
+	return 0;
+
+err_action_alloc:
+	kfree(match_value->value);
+	return -ENOMEM;
+}
+
+static int mlxsw_sp_erif_entry_get(struct mlxsw_sp *mlxsw_sp,
+				   struct devlink_dpipe_entry *entry,
+				   struct mlxsw_sp_rif *rif,
+				   bool counters_enabled)
+{
+	u32 *action_value;
+	u32 *rif_value;
+	u64 cnt;
+	int err;
+
+	/* Set Match RIF index */
+	rif_value = entry->match_values->value;
+	*rif_value = mlxsw_sp_rif_index_get(rif);
+	entry->match_values->mapping_value = mlxsw_sp_rif_dev_ifindex_get(rif);
+	entry->match_values->mapping_valid = true;
+
+	/* Set Action Forwarding */
+	action_value = entry->action_values->value;
+	*action_value = 1;
+
+	entry->counter_valid = false;
+	entry->counter = 0;
+	if (!counters_enabled)
+		return 0;
+
+	entry->index = mlxsw_sp_rif_index_get(rif);
+	err = mlxsw_sp_rif_counter_value_get(mlxsw_sp, rif,
+					     MLXSW_SP_RIF_COUNTER_EGRESS,
+					     &cnt);
+	if (!err) {
+		entry->counter = cnt;
+		entry->counter_valid = true;
+	}
+	return 0;
+}
+
+static int
+mlxsw_sp_table_erif_entries_dump(void *priv, bool counters_enabled,
+				 struct devlink_dpipe_dump_ctx *dump_ctx)
+{
+	struct devlink_dpipe_value match_value = {0}, action_value = {0};
+	struct devlink_dpipe_action action = {0};
+	struct devlink_dpipe_match match = {0};
+	struct devlink_dpipe_entry entry = {0};
+	struct mlxsw_sp *mlxsw_sp = priv;
+	unsigned int rif_count;
+	int i, j;
+	int err;
+
+	mlxsw_sp_erif_match_action_prepare(&match, &action);
+	err = mlxsw_sp_erif_entry_prepare(&entry, &match_value, &match,
+					  &action_value, &action);
+	if (err)
+		return err;
+
+	rif_count = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
+	rtnl_lock();
+	i = 0;
+start_again:
+	err = devlink_dpipe_entry_ctx_prepare(dump_ctx);
+	if (err)
+		return err;
+	j = 0;
+	for (; i < rif_count; i++) {
+		if (!mlxsw_sp->rifs[i])
+			continue;
+		err = mlxsw_sp_erif_entry_get(mlxsw_sp, &entry,
+					      mlxsw_sp->rifs[i],
+					      counters_enabled);
+		if (err)
+			goto err_entry_get;
+		err = devlink_dpipe_entry_ctx_append(dump_ctx, &entry);
+		if (err) {
+			if (err == -EMSGSIZE) {
+				if (!j)
+					goto err_entry_append;
+				break;
+			}
+			goto err_entry_append;
+		}
+		j++;
+	}
+
+	devlink_dpipe_entry_ctx_close(dump_ctx);
+	if (i != rif_count)
+		goto start_again;
+	rtnl_unlock();
+
+	mlxsw_sp_erif_entry_clear(&entry);
+	return 0;
+err_entry_append:
+err_entry_get:
+	rtnl_unlock();
+	mlxsw_sp_erif_entry_clear(&entry);
+	return err;
+}
+
+static int mlxsw_sp_table_erif_counters_update(void *priv, bool enable)
+{
+	struct mlxsw_sp *mlxsw_sp = priv;
+	int i;
+
+	rtnl_lock();
+	for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) {
+		if (!mlxsw_sp->rifs[i])
+			continue;
+		if (enable)
+			mlxsw_sp_rif_counter_alloc(mlxsw_sp,
+						   mlxsw_sp->rifs[i],
+						   MLXSW_SP_RIF_COUNTER_EGRESS);
+		else
+			mlxsw_sp_rif_counter_free(mlxsw_sp,
+						  mlxsw_sp->rifs[i],
+						  MLXSW_SP_RIF_COUNTER_EGRESS);
+	}
+	rtnl_unlock();
+	return 0;
+}
+
 static struct devlink_dpipe_table_ops mlxsw_sp_erif_ops = {
 	.matches_dump = mlxsw_sp_dpipe_table_erif_matches_dump,
 	.actions_dump = mlxsw_sp_dpipe_table_erif_actions_dump,
+	.entries_dump = mlxsw_sp_table_erif_entries_dump,
+	.counters_set_update = mlxsw_sp_table_erif_counters_update,
 };
 
 static int mlxsw_sp_dpipe_erif_table_init(struct mlxsw_sp *mlxsw_sp)
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [patch iproute2/net-next] devlink: Add support for pipeline debug (dpipe)
  2017-03-25  7:35 [patch net-next 0/8] Add support for pipeline debug (dpipe) Jiri Pirko
                   ` (7 preceding siblings ...)
  2017-03-25  7:35 ` [patch net-next 8/8] mlxsw: spectrum: Add Support for erif table entries access Jiri Pirko
@ 2017-03-25  7:37 ` Jiri Pirko
  2017-03-27 22:48 ` [patch net-next 0/8] " David Miller
  9 siblings, 0 replies; 20+ messages in thread
From: Jiri Pirko @ 2017-03-25  7:37 UTC (permalink / raw)
  To: netdev
  Cc: davem, arkadis, idosch, mlxsw, jhs, ivecera, roopa, f.fainelli,
	vivien.didelot, john.fastabend, andrew, simon.horman

From: Arkadi Sharshevsky <arkadis@mellanox.com>

Add support for pipeline debug (dpipe). The headers are used both the
gain visibillity into the headers supported by the hardware, and to
build the headers/field database which is used by other commands.

Examples:

First we can see the headers supported by the hardware:

$devlink dpipe header show pci/0000:03:00.0

pci/0000:03:00.0:
  name mlxsw_meta
  field:
    name erif_port bitwidth 32 mapping_type ifindex
    name l3_forward bitwidth 1
    name l2_drop bitwidth 1

Note that mapping_type is presented only if relevant. Also the header/
field id's are reported by the kernel they are not shown by default.
They can be observed by using the -v option. Also the headers scope
(global/local) is specified.

$devlink -v dpipe header show pci/0000:03:00.0

pci/0000:03:00.0:
  name mlxsw_meta id 0 global false
  field:
    name erif_port id 0 bitwidth 32 mapping_type ifindex
    name l3_forward id 1 bitwidth 1
    name l2_drop id 2 bitwidth 1

Second we can examine the tables supported by the hardware. In order
to dump all the tables no table name should be provided:
$devlink dpipe table show pci/0000:03:00.0

In order to examine specific table its name have to be specified:
$devlink dpipe table show pci/0000:03:00.0 name erif

pci/0000:03:00.0:
  name mlxsw_erif size 800 counters_enabled true
  match:
    type field_exact header mlxsw_meta field erif_port mapping ifindex
  action:
    type field_modify header mlxsw_meta field l3_forward
    type field_modify header mlxsw_meta field l2_drop

To enable/disable counters on the table:
$devlink dpipe table set pci/0000:03:00.0 name erif counters enable
$devlink dpipe table set pci/0000:03:00.0 name erif counters disable

In order to see the current entries in the hardware for specific table:
$devlink dpipe table dump pci/0000:03:00.0 name erif

pci/0000:03:00.0:
  index 0 counter 0
  match_value:
    type field_exact header mlxsw_meta field erif_port mapping ifindex mapping_value 383 value 0
  action_value:
    type field_modify header mlxsw_meta field l3_forward value 1

  index 1 counter 0
  match_value:
    type field_exact header mlxsw_meta field erif_port mapping ifindex mapping_value 381 value 1
  action_value:
    type field_modify header mlxsw_meta field l3_forward value 1

In the above example the table contains two entries which does match
on erif port and forwards the packet or drop it (currently only the
forward count is implemented). The counter values are provided for
example. In case the counting is not enabled on the table the counters
will not be available.

Signed-off-by: Arkadi Sharshevsky <arkadis@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
---
 devlink/devlink.c       | 1413 +++++++++++++++++++++++++++++++++++++++++++----
 include/linux/devlink.h |   67 ++-
 2 files changed, 1382 insertions(+), 98 deletions(-)

diff --git a/devlink/devlink.c b/devlink/devlink.c
index e90226e..3d525ea 100644
--- a/devlink/devlink.c
+++ b/devlink/devlink.c
@@ -34,7 +34,15 @@
 #define ESWITCH_INLINE_MODE_TRANSPORT "transport"
 
 #define pr_err(args...) fprintf(stderr, ##args)
-#define pr_out(args...) fprintf(stdout, ##args)
+#define pr_out(args...)						\
+	do {							\
+		if (g_indent_newline) {				\
+			fprintf(stdout, "%s", g_indent_str);	\
+			g_indent_newline = false;		\
+		}						\
+		fprintf(stdout, ##args);			\
+	} while (0)
+
 #define pr_out_sp(num, args...)					\
 	do {							\
 		int ret = fprintf(stdout, ##args);		\
@@ -42,6 +50,35 @@
 			fprintf(stdout, "%*s", num - ret, "");	\
 	} while (0)
 
+static int g_indent_level;
+static bool g_indent_newline;
+#define INDENT_STR_STEP 2
+#define INDENT_STR_MAXLEN 32
+static char g_indent_str[INDENT_STR_MAXLEN + 1] = "";
+
+static void __pr_out_indent_inc(void)
+{
+	if (g_indent_level + INDENT_STR_STEP > INDENT_STR_MAXLEN)
+		return;
+	g_indent_level += INDENT_STR_STEP;
+	memset(g_indent_str, ' ', sizeof(g_indent_str));
+	g_indent_str[g_indent_level] = '\0';
+}
+
+static void __pr_out_indent_dec(void)
+{
+	if (g_indent_level - INDENT_STR_STEP < 0)
+		return;
+	g_indent_level -= INDENT_STR_STEP;
+	g_indent_str[g_indent_level] = '\0';
+}
+
+static void __pr_out_newline(void)
+{
+	pr_out("\n");
+	g_indent_newline = true;
+}
+
 static int _mnlg_socket_recv_run(struct mnlg_socket *nlg,
 				 mnl_cb_t data_cb, void *data)
 {
@@ -137,6 +174,8 @@ static void ifname_map_free(struct ifname_map *ifname_map)
 #define DL_OPT_SB_TC		BIT(10)
 #define DL_OPT_ESWITCH_MODE	BIT(11)
 #define DL_OPT_ESWITCH_INLINE_MODE	BIT(12)
+#define DL_OPT_DPIPE_TABLE_NAME	BIT(13)
+#define DL_OPT_DPIPE_TABLE_COUNTERS	BIT(14)
 
 struct dl_opts {
 	uint32_t present; /* flags of present items */
@@ -154,6 +193,8 @@ struct dl_opts {
 	uint16_t sb_tc_index;
 	enum devlink_eswitch_mode eswitch_mode;
 	enum devlink_eswitch_inline_mode eswitch_inline_mode;
+	const char *dpipe_table_name;
+	bool dpipe_counters_enable;
 };
 
 struct dl {
@@ -166,6 +207,7 @@ struct dl {
 	json_writer_t *jw;
 	bool json_output;
 	bool pretty_output;
+	bool verbose;
 	struct {
 		bool present;
 		char *bus_name;
@@ -314,6 +356,102 @@ static int attr_cb(const struct nlattr *attr, void *data)
 	if (type == DEVLINK_ATTR_ESWITCH_INLINE_MODE &&
 	    mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
 		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_TABLES &&
+	    mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_TABLE &&
+	    mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_TABLE_NAME &&
+	    mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_TABLE_SIZE &&
+	    mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_TABLE_MATCHES &&
+	    mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_TABLE_ACTIONS &&
+	    mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED &&
+	    mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
+		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_ENTRIES &&
+	    mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_ENTRY &&
+	    mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_ENTRY_INDEX &&
+	    mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_ENTRY_MATCH_VALUES &&
+	    mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_ENTRY_ACTION_VALUES &&
+	    mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_ENTRY_COUNTER &&
+	    mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_MATCH &&
+	    mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_MATCH_VALUE &&
+	    mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_MATCH_TYPE &&
+	    mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_ACTION &&
+	    mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_ACTION_VALUE &&
+	    mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_ACTION_TYPE &&
+	    mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_VALUE_MAPPING &&
+	    mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_HEADERS &&
+	    mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_HEADER &&
+	    mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_HEADER_NAME &&
+	    mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_HEADER_ID &&
+	    mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_HEADER_FIELDS &&
+	    mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_HEADER_GLOBAL &&
+	    mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
+		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_HEADER_INDEX &&
+	    mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_FIELD &&
+	    mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_FIELD_NAME &&
+	    mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_FIELD_ID &&
+	    mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_FIELD_BITWIDTH &&
+	    mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+		return MNL_CB_ERROR;
+	if (type == DEVLINK_ATTR_DPIPE_FIELD_MAPPING_TYPE &&
+	    mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+		return MNL_CB_ERROR;
 	tb[type] = attr;
 	return MNL_CB_OK;
 }
@@ -709,6 +847,20 @@ static int eswitch_inline_mode_get(const char *typestr,
 	return 0;
 }
 
+static int dpipe_counters_enable_get(const char *typestr,
+				     bool *counters_enable)
+{
+	if (strcmp(typestr, "enable") == 0) {
+		*counters_enable = 1;
+	} else if (strcmp(typestr, "disable") == 0) {
+		*counters_enable = 0;
+	} else {
+		pr_err("Unknown counter_state \"%s\"\n", typestr);
+		return -EINVAL;
+	}
+	return 0;
+}
+
 static int dl_argv_parse(struct dl *dl, uint32_t o_required,
 			 uint32_t o_optional)
 {
@@ -843,6 +995,27 @@ static int dl_argv_parse(struct dl *dl, uint32_t o_required,
 			if (err)
 				return err;
 			o_found |= DL_OPT_ESWITCH_INLINE_MODE;
+		} else if (dl_argv_match(dl, "name") &&
+			   (o_all & DL_OPT_DPIPE_TABLE_NAME)) {
+			dl_arg_inc(dl);
+			err = dl_argv_str(dl, &opts->dpipe_table_name);
+			if (err)
+				return err;
+			o_found |= DL_OPT_DPIPE_TABLE_NAME;
+		} else if (dl_argv_match(dl, "counters") &&
+			   (o_all & DL_OPT_DPIPE_TABLE_COUNTERS)) {
+			const char *typestr;
+
+			dl_arg_inc(dl);
+			err = dl_argv_str(dl, &typestr);
+			if (err)
+				return err;
+			err = dpipe_counters_enable_get(typestr,
+							&opts->dpipe_counters_enable);
+			if (err)
+				return err;
+			o_found |= DL_OPT_DPIPE_TABLE_COUNTERS;
+
 		} else {
 			pr_err("Unknown option \"%s\"\n", dl_argv(dl));
 			return -EINVAL;
@@ -909,6 +1082,17 @@ static int dl_argv_parse(struct dl *dl, uint32_t o_required,
 		return -EINVAL;
 	}
 
+	if ((o_required & DL_OPT_DPIPE_TABLE_NAME) &&
+	    !(o_found & DL_OPT_DPIPE_TABLE_NAME)) {
+		pr_err("Dpipe table name expected\n");
+		return -EINVAL;
+	}
+
+	if ((o_required & DL_OPT_DPIPE_TABLE_COUNTERS) &&
+	    !(o_found & DL_OPT_DPIPE_TABLE_COUNTERS)) {
+		pr_err("Dpipe table counter state expected\n");
+		return -EINVAL;
+	}
 	return 0;
 }
 
@@ -958,6 +1142,12 @@ static void dl_opts_put(struct nlmsghdr *nlh, struct dl *dl)
 	if (opts->present & DL_OPT_ESWITCH_INLINE_MODE)
 		mnl_attr_put_u8(nlh, DEVLINK_ATTR_ESWITCH_INLINE_MODE,
 				opts->eswitch_inline_mode);
+	if (opts->present & DL_OPT_DPIPE_TABLE_NAME)
+		mnl_attr_put_strz(nlh, DEVLINK_ATTR_DPIPE_TABLE_NAME,
+				  opts->dpipe_table_name);
+	if (opts->present & DL_OPT_DPIPE_TABLE_COUNTERS)
+		mnl_attr_put_u8(nlh, DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED,
+				opts->dpipe_counters_enable);
 }
 
 static int dl_argv_parse_put(struct nlmsghdr *nlh, struct dl *dl,
@@ -1076,7 +1266,19 @@ static void __pr_out_handle_start(struct dl *dl, struct nlattr **tb,
 			jsonw_start_object(dl->jw);
 		}
 	} else {
-		pr_out("%s%s", buf, content ? ":" : "");
+		if (array) {
+			if (should_arr_last_handle_end(dl, bus_name, dev_name))
+				__pr_out_indent_dec();
+			if (should_arr_last_handle_start(dl, bus_name,
+							 dev_name)) {
+				pr_out("%s%s", buf, content ? ":" : "");
+				__pr_out_newline();
+				__pr_out_indent_inc();
+				arr_last_handle_set(dl, bus_name, dev_name);
+			}
+		} else {
+			pr_out("%s%s", buf, content ? ":" : "");
+		}
 	}
 }
 
@@ -1090,7 +1292,7 @@ static void pr_out_handle_end(struct dl *dl)
 	if (dl->json_output)
 		jsonw_end_object(dl->jw);
 	else
-		pr_out("\n");
+		__pr_out_newline();
 }
 
 static void pr_out_handle(struct dl *dl, struct nlattr **tb)
@@ -1206,18 +1408,26 @@ static void pr_out_port_handle_end(struct dl *dl)
 
 static void pr_out_str(struct dl *dl, const char *name, const char *val)
 {
-	if (dl->json_output)
+	if (dl->json_output) {
 		jsonw_string_field(dl->jw, name, val);
-	else
-		pr_out(" %s %s", name, val);
+	} else {
+		if (g_indent_newline)
+			pr_out("%s %s", name, val);
+		else
+			pr_out(" %s %s", name, val);
+	}
 }
 
 static void pr_out_uint(struct dl *dl, const char *name, unsigned int val)
 {
-	if (dl->json_output)
+	if (dl->json_output) {
 		jsonw_uint_field(dl->jw, name, val);
-	else
-		pr_out(" %s %u", name, val);
+	} else {
+		if (g_indent_newline)
+			pr_out("%s %u", name, val);
+		else
+			pr_out(" %s %u", name, val);
+	}
 }
 
 static void pr_out_dev(struct dl *dl, struct nlattr **tb)
@@ -1244,6 +1454,42 @@ static void pr_out_section_end(struct dl *dl)
 	}
 }
 
+static void pr_out_array_start(struct dl *dl, const char *name)
+{
+	if (dl->json_output) {
+		jsonw_name(dl->jw, name);
+		jsonw_start_array(dl->jw);
+	} else {
+		if (!g_indent_newline)
+			__pr_out_newline();
+		pr_out("%s:", name);
+		__pr_out_newline();
+		__pr_out_indent_inc();
+	}
+}
+
+static void pr_out_array_end(struct dl *dl)
+{
+	if (dl->json_output)
+		jsonw_end_array(dl->jw);
+	else
+		__pr_out_indent_dec();
+}
+
+static void pr_out_entry_start(struct dl *dl)
+{
+	if (dl->json_output)
+		jsonw_start_object(dl->jw);
+}
+
+static void pr_out_entry_end(struct dl *dl)
+{
+	if (dl->json_output)
+		jsonw_end_object(dl->jw);
+	else
+		__pr_out_newline();
+}
+
 static const char *eswitch_mode_name(uint32_t mode)
 {
 	switch (mode) {
@@ -2466,130 +2712,1103 @@ static int cmd_mon(struct dl *dl)
 	return -ENOENT;
 }
 
-static void help(void)
+struct dpipe_field {
+	char *name;
+	unsigned int id;
+	unsigned int bitwidth;
+	enum devlink_dpipe_field_mapping_type mapping_type;
+};
+
+struct dpipe_header {
+	struct list_head list;
+	char *name;
+	unsigned int id;
+	struct dpipe_field *fields;
+	unsigned int fields_count;
+};
+
+struct dpipe_ctx {
+	struct dl *dl;
+	int err;
+	struct list_head global_headers;
+	struct list_head local_headers;
+	bool print_headers;
+};
+
+static struct dpipe_header *dpipe_header_alloc(unsigned int fields_count)
 {
-	pr_err("Usage: devlink [ OPTIONS ] OBJECT { COMMAND | help }\n"
-	       "where  OBJECT := { dev | port | sb | monitor }\n"
-	       "       OPTIONS := { -V[ersion] | -n[no-nice-names] | -j[json] | -p[pretty] }\n");
+	struct dpipe_header *header;
+
+	header = calloc(1, sizeof(struct dpipe_header));
+	if (!header)
+		return NULL;
+	header->fields = calloc(fields_count, sizeof(struct dpipe_field));
+	if (!header->fields)
+		goto err_fields_alloc;
+	header->fields_count = fields_count;
+	return header;
+
+err_fields_alloc:
+	free(header);
+	return NULL;
 }
 
-static int dl_cmd(struct dl *dl)
+static void dpipe_header_free(struct dpipe_header *header)
 {
-	if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
-		help();
-		return 0;
-	} else if (dl_argv_match(dl, "dev")) {
-		dl_arg_inc(dl);
-		return cmd_dev(dl);
-	} else if (dl_argv_match(dl, "port")) {
-		dl_arg_inc(dl);
-		return cmd_port(dl);
-	} else if (dl_argv_match(dl, "sb")) {
-		dl_arg_inc(dl);
-		return cmd_sb(dl);
-	} else if (dl_argv_match(dl, "monitor")) {
-		dl_arg_inc(dl);
-		return cmd_mon(dl);
+	free(header->fields);
+	free(header);
+}
+
+static void dpipe_header_clear(struct dpipe_header *header)
+{
+	struct dpipe_field *field;
+	int i;
+
+	for (i = 0; i < header->fields_count; i++) {
+		field = &header->fields[i];
+		free(field->name);
 	}
-	pr_err("Object \"%s\" not found\n", dl_argv(dl));
-	return -ENOENT;
+	free(header->name);
 }
 
-static int dl_init(struct dl *dl, int argc, char **argv)
+static void dpipe_header_add(struct dpipe_ctx *ctx,
+			     struct dpipe_header *header, bool global)
 {
-	int err;
+	if (global)
+		list_add(&header->list, &ctx->global_headers);
+	else
+		list_add(&header->list, &ctx->local_headers);
+}
 
-	dl->argc = argc;
-	dl->argv = argv;
+static void dpipe_header_del(struct dpipe_header *header)
+{
+	list_del(&header->list);
+}
 
-	dl->nlg = mnlg_socket_open(DEVLINK_GENL_NAME, DEVLINK_GENL_VERSION);
-	if (!dl->nlg) {
-		pr_err("Failed to connect to devlink Netlink\n");
-		return -errno;
-	}
+static struct dpipe_ctx *dpipe_ctx_alloc(struct dl *dl)
+{
+	struct dpipe_ctx *ctx;
 
-	err = ifname_map_init(dl);
-	if (err) {
-		pr_err("Failed to create index map\n");
-		goto err_ifname_map_create;
+	ctx = calloc(1, sizeof(struct dpipe_ctx));
+	if (!ctx)
+		return NULL;
+	ctx->dl = dl;
+	INIT_LIST_HEAD(&ctx->global_headers);
+	INIT_LIST_HEAD(&ctx->local_headers);
+	return ctx;
+}
+
+static void dpipe_ctx_free(struct dpipe_ctx *ctx)
+{
+	free(ctx);
+}
+
+static void dpipe_ctx_clear(struct dpipe_ctx *ctx)
+{
+	struct dpipe_header *header, *tmp;
+
+	list_for_each_entry_safe(header, tmp, &ctx->global_headers,
+				 list) {
+		dpipe_header_del(header);
+		dpipe_header_clear(header);
+		dpipe_header_free(header);
 	}
-	if (dl->json_output) {
-		dl->jw = jsonw_new(stdout);
-		if (!dl->jw) {
-			pr_err("Failed to create JSON writer\n");
-			goto err_json_new;
-		}
-		jsonw_pretty(dl->jw, dl->pretty_output);
+	list_for_each_entry_safe(header, tmp, &ctx->local_headers,
+				 list) {
+		dpipe_header_del(header);
+		dpipe_header_clear(header);
+		dpipe_header_free(header);
 	}
-	return 0;
-
-err_json_new:
-	ifname_map_fini(dl);
-err_ifname_map_create:
-	mnlg_socket_close(dl->nlg);
-	return err;
 }
 
-static void dl_fini(struct dl *dl)
+static const char *dpipe_header_id2s(struct dpipe_ctx *ctx,
+				     uint32_t header_id, bool global)
 {
-	if (dl->json_output)
-		jsonw_destroy(&dl->jw);
-	ifname_map_fini(dl);
-	mnlg_socket_close(dl->nlg);
+	struct list_head *header_list;
+	struct dpipe_header *header;
+
+	if (global)
+		header_list = &ctx->global_headers;
+	else
+		header_list = &ctx->local_headers;
+	list_for_each_entry(header, header_list, list) {
+		if (header->id != header_id)
+			continue;
+		return header->name;
+	}
+	return NULL;
 }
 
-static struct dl *dl_alloc(void)
+static const char *dpipe_field_id2s(struct dpipe_ctx *ctx,
+				    uint32_t header_id,
+				    uint32_t field_id, bool global)
 {
-	struct dl *dl;
+	struct list_head *header_list;
+	struct dpipe_header *header;
 
-	dl = calloc(1, sizeof(*dl));
-	if (!dl)
+	if (global)
+		header_list = &ctx->global_headers;
+	else
+		header_list = &ctx->local_headers;
+	list_for_each_entry(header, header_list, list) {
+		if (header->id != header_id)
+			continue;
+		return header->fields[field_id].name;
+	}
+	return NULL;
+}
+
+static const char *
+dpipe_field_mapping_e2s(enum devlink_dpipe_field_mapping_type mapping_type)
+{
+	switch (mapping_type) {
+	case DEVLINK_DPIPE_FIELD_MAPPING_TYPE_NONE:
 		return NULL;
-	return dl;
+	case DEVLINK_DPIPE_FIELD_MAPPING_TYPE_IFINDEX:
+		return "ifindex";
+	default:
+		return "<unknown>";
+	}
 }
 
-static void dl_free(struct dl *dl)
+static const char *
+dpipe_mapping_get(struct dpipe_ctx *ctx, uint32_t header_id,
+		  uint32_t field_id, bool global)
 {
-	free(dl);
+	enum devlink_dpipe_field_mapping_type mapping_type;
+	struct list_head *header_list;
+	struct dpipe_header *header;
+
+	if (global)
+		header_list = &ctx->global_headers;
+	else
+		header_list = &ctx->local_headers;
+	list_for_each_entry(header, header_list, list) {
+		if (header->id != header_id)
+			continue;
+		mapping_type = header->fields[field_id].mapping_type;
+		return dpipe_field_mapping_e2s(mapping_type);
+	}
+	return NULL;
 }
 
-int main(int argc, char **argv)
+static void pr_out_dpipe_fields(struct dpipe_ctx *ctx,
+				struct dpipe_field *fields,
+				unsigned int field_count)
 {
-	static const struct option long_options[] = {
-		{ "Version",		no_argument,		NULL, 'V' },
-		{ "no-nice-names",	no_argument,		NULL, 'n' },
-		{ "json",		no_argument,		NULL, 'j' },
-		{ "pretty",		no_argument,		NULL, 'p' },
-		{ NULL, 0, NULL, 0 }
-	};
-	struct dl *dl;
-	int opt;
-	int err;
-	int ret;
+	struct dpipe_field *field;
+	int i;
 
-	dl = dl_alloc();
-	if (!dl) {
-		pr_err("Failed to allocate memory for devlink\n");
-		return EXIT_FAILURE;
+	for (i = 0; i < field_count; i++) {
+		field = &fields[i];
+		pr_out_entry_start(ctx->dl);
+		pr_out_str(ctx->dl, "name", field->name);
+		if (ctx->dl->verbose)
+			pr_out_uint(ctx->dl, "id", field->id);
+		pr_out_uint(ctx->dl, "bitwidth", field->bitwidth);
+		if (field->mapping_type)
+			pr_out_str(ctx->dl, "mapping_type",
+				   dpipe_field_mapping_e2s(field->mapping_type));
+		pr_out_entry_end(ctx->dl);
 	}
+}
 
-	while ((opt = getopt_long(argc, argv, "Vnjp",
-				  long_options, NULL)) >= 0) {
+static void
+pr_out_dpipe_header(struct dpipe_ctx *ctx, struct nlattr **tb,
+		    struct dpipe_header *header, bool global)
+{
+	pr_out_handle_start_arr(ctx->dl, tb);
+	pr_out_str(ctx->dl, "name", header->name);
+	if (ctx->dl->verbose) {
+		pr_out_uint(ctx->dl, "id", header->id);
+		pr_out_str(ctx->dl, "global",
+			   global ? "true" : "false");
+	}
+	pr_out_array_start(ctx->dl, "field");
+	pr_out_dpipe_fields(ctx, header->fields,
+			    header->fields_count);
+	pr_out_array_end(ctx->dl);
+	pr_out_handle_end(ctx->dl);
+}
 
-		switch (opt) {
-		case 'V':
-			printf("devlink utility, iproute2-ss%s\n", SNAPSHOT);
-			ret = EXIT_SUCCESS;
-			goto dl_free;
-		case 'n':
-			dl->no_nice_names = true;
-			break;
-		case 'j':
+static void pr_out_dpipe_headers(struct dpipe_ctx *ctx,
+				 struct nlattr **tb)
+{
+	struct dpipe_header *header;
+
+	list_for_each_entry(header, &ctx->local_headers, list)
+		pr_out_dpipe_header(ctx, tb, header, false);
+
+	list_for_each_entry(header, &ctx->global_headers, list)
+		pr_out_dpipe_header(ctx, tb, header, true);
+}
+
+static int dpipe_header_field_get(struct nlattr *nl, struct dpipe_field *field)
+{
+	struct nlattr *nla_field[DEVLINK_ATTR_MAX + 1] = {};
+	const char *name;
+	int err;
+
+	err = mnl_attr_parse_nested(nl, attr_cb, nla_field);
+	if (err != MNL_CB_OK)
+		return -EINVAL;
+	if (!nla_field[DEVLINK_ATTR_DPIPE_FIELD_ID] ||
+	    !nla_field[DEVLINK_ATTR_DPIPE_FIELD_NAME] ||
+	    !nla_field[DEVLINK_ATTR_DPIPE_FIELD_BITWIDTH] ||
+	    !nla_field[DEVLINK_ATTR_DPIPE_FIELD_MAPPING_TYPE])
+		return -EINVAL;
+
+	name = mnl_attr_get_str(nla_field[DEVLINK_ATTR_DPIPE_FIELD_NAME]);
+	field->id = mnl_attr_get_u32(nla_field[DEVLINK_ATTR_DPIPE_FIELD_ID]);
+	field->bitwidth = mnl_attr_get_u32(nla_field[DEVLINK_ATTR_DPIPE_FIELD_BITWIDTH]);
+	field->name = strdup(name);
+	if (!field->name)
+		return -ENOMEM;
+	field->mapping_type = mnl_attr_get_u32(nla_field[DEVLINK_ATTR_DPIPE_FIELD_MAPPING_TYPE]);
+	return 0;
+}
+
+static int dpipe_header_fields_get(struct nlattr *nla_fields,
+				   struct dpipe_field *fields)
+{
+	struct nlattr *nla_field;
+	int count = 0;
+	int err;
+
+	mnl_attr_for_each_nested(nla_field, nla_fields) {
+		err = dpipe_header_field_get(nla_field, &fields[count]);
+		if (err)
+			return err;
+		count++;
+	}
+	return 0;
+}
+
+static unsigned int dpipe_header_field_count_get(struct nlattr *nla_fields)
+{
+	struct nlattr *nla_field;
+	unsigned int count = 0;
+
+	mnl_attr_for_each_nested(nla_field, nla_fields)
+		count++;
+	return count;
+}
+
+static int dpipe_header_get(struct dpipe_ctx *ctx, struct nlattr *nl)
+{
+	struct nlattr *nla_header[DEVLINK_ATTR_MAX + 1] = {};
+	struct dpipe_header *header;
+	unsigned int fields_count;
+	const char *header_name;
+	bool global;
+	int err;
+
+	err = mnl_attr_parse_nested(nl, attr_cb, nla_header);
+	if (err != MNL_CB_OK)
+		return -EINVAL;
+
+	if (!nla_header[DEVLINK_ATTR_DPIPE_HEADER_NAME] ||
+	    !nla_header[DEVLINK_ATTR_DPIPE_HEADER_ID] ||
+	    !nla_header[DEVLINK_ATTR_DPIPE_HEADER_FIELDS])
+		return -EINVAL;
+
+	fields_count = dpipe_header_field_count_get(nla_header[DEVLINK_ATTR_DPIPE_HEADER_FIELDS]);
+	header = dpipe_header_alloc(fields_count);
+	if (!header)
+		return -ENOMEM;
+
+	header_name = mnl_attr_get_str(nla_header[DEVLINK_ATTR_DPIPE_HEADER_NAME]);
+	header->name = strdup(header_name);
+	header->id = mnl_attr_get_u32(nla_header[DEVLINK_ATTR_DPIPE_HEADER_ID]);
+	header->fields_count = fields_count;
+	global = !!mnl_attr_get_u8(nla_header[DEVLINK_ATTR_DPIPE_HEADER_GLOBAL]);
+
+	err = dpipe_header_fields_get(nla_header[DEVLINK_ATTR_DPIPE_HEADER_FIELDS],
+				      header->fields);
+	if (err)
+		goto err_field_get;
+	dpipe_header_add(ctx, header, global);
+	return 0;
+
+err_field_get:
+	dpipe_header_free(header);
+	return err;
+}
+
+static int dpipe_headers_get(struct dpipe_ctx *ctx, struct nlattr **tb)
+{
+	struct nlattr *nla_headers = tb[DEVLINK_ATTR_DPIPE_HEADERS];
+	struct nlattr *nla_header;
+	int err;
+
+	mnl_attr_for_each_nested(nla_header, nla_headers) {
+		err = dpipe_header_get(ctx, nla_header);
+		if (err)
+			return err;
+	}
+	return 0;
+}
+
+static int cmd_dpipe_header_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct dpipe_ctx *ctx = data;
+	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
+	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+	int err;
+
+	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
+	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
+	    !tb[DEVLINK_ATTR_DPIPE_HEADERS])
+		return MNL_CB_ERROR;
+	err = dpipe_headers_get(ctx, tb);
+	if (err) {
+		ctx->err = err;
+		return MNL_CB_ERROR;
+	}
+
+	if (ctx->print_headers)
+		pr_out_dpipe_headers(ctx, tb);
+	return MNL_CB_OK;
+}
+
+static int cmd_dpipe_headers_show(struct dl *dl)
+{
+	struct nlmsghdr *nlh;
+	struct dpipe_ctx *ctx;
+	uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
+	int err;
+
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_HEADERS_GET, flags);
+
+	err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, 0);
+	if (err)
+		return err;
+
+	ctx = dpipe_ctx_alloc(dl);
+	if (!ctx)
+		return -ENOMEM;
+
+	ctx->print_headers = true;
+
+	pr_out_section_start(dl, "header");
+	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_header_cb, ctx);
+	if (err)
+		pr_err("error get headers %s\n", strerror(ctx->err));
+	pr_out_section_end(dl);
+
+	dpipe_ctx_clear(ctx);
+	dpipe_ctx_free(ctx);
+	return err;
+}
+
+static void cmd_dpipe_header_help(void)
+{
+	pr_err("Usage: devlink dpipe headers show DEV\n");
+}
+
+static int cmd_dpipe_header(struct dl *dl)
+{
+	if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
+		cmd_dpipe_header_help();
+		return 0;
+	} else if (dl_argv_match(dl, "show")) {
+		dl_arg_inc(dl);
+		return cmd_dpipe_headers_show(dl);
+	}
+	pr_err("Command \"%s\" not found\n", dl_argv(dl));
+	return -ENOENT;
+}
+
+static const char
+*dpipe_action_type_e2s(enum devlink_dpipe_action_type action_type)
+{
+	switch (action_type) {
+	case DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY:
+		return "field_modify";
+	default:
+		return "<unknown>";
+	}
+}
+
+static void pr_out_dpipe_action(struct dpipe_ctx *ctx,
+				uint32_t header_id, uint32_t field_id,
+				uint32_t action_type, bool global)
+{
+	const char *mapping;
+
+	pr_out_str(ctx->dl, "type", dpipe_action_type_e2s(action_type));
+	pr_out_str(ctx->dl, "header", dpipe_header_id2s(ctx, header_id,
+							global));
+	pr_out_str(ctx->dl, "field", dpipe_field_id2s(ctx, header_id, field_id,
+						      global));
+	mapping = dpipe_mapping_get(ctx, header_id, field_id, global);
+	if (mapping)
+		pr_out_str(ctx->dl, "mapping", mapping);
+}
+
+static int dpipe_action_show(struct dpipe_ctx *ctx, struct nlattr *nl)
+{
+	struct nlattr *nla_action[DEVLINK_ATTR_MAX + 1] = {};
+	uint32_t header_id, field_id, action_type;
+	bool global;
+	int err;
+
+	err = mnl_attr_parse_nested(nl, attr_cb, nla_action);
+	if (err != MNL_CB_OK)
+		return -EINVAL;
+
+	if (!nla_action[DEVLINK_ATTR_DPIPE_ACTION_TYPE] ||
+	    !nla_action[DEVLINK_ATTR_DPIPE_HEADER_INDEX] ||
+	    !nla_action[DEVLINK_ATTR_DPIPE_HEADER_ID] ||
+	    !nla_action[DEVLINK_ATTR_DPIPE_FIELD_ID]) {
+		return -EINVAL;
+	}
+
+	header_id = mnl_attr_get_u32(nla_action[DEVLINK_ATTR_DPIPE_HEADER_ID]);
+	field_id = mnl_attr_get_u32(nla_action[DEVLINK_ATTR_DPIPE_FIELD_ID]);
+	action_type = mnl_attr_get_u32(nla_action[DEVLINK_ATTR_DPIPE_ACTION_TYPE]);
+	global = !!mnl_attr_get_u8(nla_action[DEVLINK_ATTR_DPIPE_HEADER_GLOBAL]);
+
+	pr_out_dpipe_action(ctx, header_id, field_id, action_type, global);
+	return 0;
+}
+
+static int dpipe_table_actions_show(struct dpipe_ctx *ctx,
+				    struct nlattr *nla_actions)
+{
+	struct nlattr *nla_action;
+
+	mnl_attr_for_each_nested(nla_action, nla_actions) {
+		pr_out_entry_start(ctx->dl);
+		if (dpipe_action_show(ctx, nla_action))
+			goto err_action_show;
+		pr_out_entry_end(ctx->dl);
+	}
+	return 0;
+
+err_action_show:
+	pr_out_entry_end(ctx->dl);
+	return -EINVAL;
+}
+
+static const char *
+dpipe_match_type_e2s(enum devlink_dpipe_match_type match_type)
+{
+	switch (match_type) {
+	case DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT:
+		return "field_exact";
+	default:
+		return "<unknown>";
+	}
+}
+
+static void pr_out_dpipe_match(struct dpipe_ctx *ctx,
+			       uint32_t header_id, uint32_t field_id,
+			       uint32_t match_type, bool global)
+{
+	const char *mapping;
+
+	pr_out_str(ctx->dl, "type", dpipe_match_type_e2s(match_type));
+	pr_out_str(ctx->dl, "header", dpipe_header_id2s(ctx, header_id,
+							global));
+	pr_out_str(ctx->dl, "field", dpipe_field_id2s(ctx, header_id, field_id,
+						      global));
+	mapping = dpipe_mapping_get(ctx, header_id, field_id, global);
+	if (mapping)
+		pr_out_str(ctx->dl, "mapping", mapping);
+
+}
+
+static int dpipe_match_show(struct dpipe_ctx *ctx, struct nlattr *nl)
+{
+	struct nlattr *nla_match[DEVLINK_ATTR_MAX + 1] = {};
+	uint32_t header_id, field_id, match_type;
+	bool global;
+	int err;
+
+	err = mnl_attr_parse_nested(nl, attr_cb, nla_match);
+	if (err != MNL_CB_OK)
+		return -EINVAL;
+
+	if (!nla_match[DEVLINK_ATTR_DPIPE_MATCH_TYPE] ||
+	    !nla_match[DEVLINK_ATTR_DPIPE_HEADER_INDEX] ||
+	    !nla_match[DEVLINK_ATTR_DPIPE_HEADER_ID] ||
+	    !nla_match[DEVLINK_ATTR_DPIPE_FIELD_ID]) {
+		return -EINVAL;
+	}
+
+	match_type = mnl_attr_get_u32(nla_match[DEVLINK_ATTR_DPIPE_MATCH_TYPE]);
+	header_id = mnl_attr_get_u32(nla_match[DEVLINK_ATTR_DPIPE_HEADER_ID]);
+	field_id = mnl_attr_get_u32(nla_match[DEVLINK_ATTR_DPIPE_FIELD_ID]);
+	global = !!mnl_attr_get_u8(nla_match[DEVLINK_ATTR_DPIPE_HEADER_GLOBAL]);
+
+	pr_out_dpipe_match(ctx, header_id, field_id, match_type, global);
+	return 0;
+}
+
+static int dpipe_table_matches_show(struct dpipe_ctx *ctx,
+				    struct nlattr *nla_matches)
+{
+	struct nlattr *nla_match;
+
+	mnl_attr_for_each_nested(nla_match, nla_matches) {
+		pr_out_entry_start(ctx->dl);
+		if (dpipe_match_show(ctx, nla_match))
+			goto err_match_show;
+		pr_out_entry_end(ctx->dl);
+	}
+	return 0;
+
+err_match_show:
+	pr_out_entry_end(ctx->dl);
+	return -EINVAL;
+}
+
+static int dpipe_table_show(struct dpipe_ctx *ctx, struct nlattr *nl)
+{
+	struct nlattr *nla_table[DEVLINK_ATTR_MAX + 1] = {};
+	bool counters_enabled;
+	const char *name;
+	uint32_t size;
+	int err;
+
+	err = mnl_attr_parse_nested(nl, attr_cb, nla_table);
+	if (err != MNL_CB_OK)
+		return -EINVAL;
+
+	if (!nla_table[DEVLINK_ATTR_DPIPE_TABLE_NAME] ||
+	    !nla_table[DEVLINK_ATTR_DPIPE_TABLE_SIZE] ||
+	    !nla_table[DEVLINK_ATTR_DPIPE_TABLE_ACTIONS] ||
+	    !nla_table[DEVLINK_ATTR_DPIPE_TABLE_MATCHES] ||
+	    !nla_table[DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED]) {
+		return -EINVAL;
+	}
+
+	name = mnl_attr_get_str(nla_table[DEVLINK_ATTR_DPIPE_TABLE_NAME]);
+	size = mnl_attr_get_u32(nla_table[DEVLINK_ATTR_DPIPE_TABLE_SIZE]);
+	counters_enabled = !!mnl_attr_get_u8(nla_table[DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED]);
+
+	pr_out_str(ctx->dl, "name", name);
+	pr_out_uint(ctx->dl, "size", size);
+	pr_out_str(ctx->dl, "counters_enabled",
+		   counters_enabled ? "true" : "false");
+
+	pr_out_array_start(ctx->dl, "match");
+	if (dpipe_table_matches_show(ctx, nla_table[DEVLINK_ATTR_DPIPE_TABLE_MATCHES]))
+		goto err_matches_show;
+	pr_out_array_end(ctx->dl);
+
+	pr_out_array_start(ctx->dl, "action");
+	if (dpipe_table_actions_show(ctx, nla_table[DEVLINK_ATTR_DPIPE_TABLE_ACTIONS]))
+		goto err_actions_show;
+	pr_out_array_end(ctx->dl);
+
+	return 0;
+
+err_actions_show:
+err_matches_show:
+	pr_out_array_end(ctx->dl);
+	return -EINVAL;
+}
+
+static int dpipe_tables_show(struct dpipe_ctx *ctx, struct nlattr **tb)
+{
+	struct nlattr *nla_tables = tb[DEVLINK_ATTR_DPIPE_TABLES];
+	struct nlattr *nla_table;
+
+	mnl_attr_for_each_nested(nla_table, nla_tables) {
+		pr_out_handle_start_arr(ctx->dl, tb);
+		if (dpipe_table_show(ctx, nla_table))
+			goto err_table_show;
+		pr_out_handle_end(ctx->dl);
+	}
+	return 0;
+
+err_table_show:
+	pr_out_handle_end(ctx->dl);
+	return -EINVAL;
+}
+
+static int cmd_dpipe_table_show_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct dpipe_ctx *ctx = data;
+	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
+	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+
+	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
+	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
+	    !tb[DEVLINK_ATTR_DPIPE_TABLES])
+		return MNL_CB_ERROR;
+
+	if (dpipe_tables_show(ctx, tb))
+		return MNL_CB_ERROR;
+	return MNL_CB_OK;
+}
+
+static int cmd_dpipe_table_show(struct dl *dl)
+{
+	struct nlmsghdr *nlh;
+	struct dpipe_ctx *ctx;
+	uint16_t flags = NLM_F_REQUEST;
+	int err;
+
+	ctx = dpipe_ctx_alloc(dl);
+	if (!ctx)
+		return -ENOMEM;
+
+	err = dl_argv_parse(dl, DL_OPT_HANDLE, DL_OPT_DPIPE_TABLE_NAME);
+	if (err)
+		goto out;
+
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_HEADERS_GET, flags);
+	dl_opts_put(nlh, dl);
+	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_header_cb, ctx);
+	if (err) {
+		pr_err("error get headers %s\n", strerror(ctx->err));
+		goto out;
+	}
+
+	flags = NLM_F_REQUEST | NLM_F_ACK;
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_TABLE_GET, flags);
+	dl_opts_put(nlh, dl);
+
+	pr_out_section_start(dl, "table");
+	_mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_table_show_cb, ctx);
+	pr_out_section_end(dl);
+out:
+	dpipe_ctx_clear(ctx);
+	dpipe_ctx_free(ctx);
+	return err;
+}
+
+static int cmd_dpipe_table_set(struct dl *dl)
+{
+	struct nlmsghdr *nlh;
+	int err;
+
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_TABLE_COUNTERS_SET,
+			       NLM_F_REQUEST | NLM_F_ACK);
+
+	err = dl_argv_parse_put(nlh, dl,
+				DL_OPT_HANDLE | DL_OPT_DPIPE_TABLE_NAME |
+				DL_OPT_DPIPE_TABLE_COUNTERS, 0);
+	if (err)
+		return err;
+
+	return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
+}
+
+static int dpipe_entry_value_show(struct dpipe_ctx *ctx,
+				  struct nlattr **nla_match_value)
+{
+	uint16_t value_len;
+	bool mask, mapping;
+
+	mask = !!nla_match_value[DEVLINK_ATTR_DPIPE_VALUE_MASK];
+	mapping = !!nla_match_value[DEVLINK_ATTR_DPIPE_VALUE_MAPPING];
+
+	value_len = mnl_attr_get_payload_len(nla_match_value[DEVLINK_ATTR_DPIPE_VALUE]);
+	if (value_len == sizeof(uint32_t)) {
+		uint32_t value, value_mask, value_mapping;
+
+		if (mapping) {
+			value_mapping = mnl_attr_get_u32(nla_match_value[DEVLINK_ATTR_DPIPE_VALUE_MAPPING]);
+			pr_out_uint(ctx->dl, "mapping_value", value_mapping);
+		}
+
+		if (mask) {
+			value_mask = mnl_attr_get_u32(nla_match_value[DEVLINK_ATTR_DPIPE_VALUE_MASK]);
+			pr_out_uint(ctx->dl, "mask_value", value_mask);
+		}
+
+		value = mnl_attr_get_u32(nla_match_value[DEVLINK_ATTR_DPIPE_VALUE]);
+		pr_out_uint(ctx->dl, "value", value);
+
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int dpipe_entry_match_value_show(struct dpipe_ctx *ctx,
+					struct nlattr *nl)
+{
+	struct nlattr *nla_match_value[DEVLINK_ATTR_MAX + 1] = {};
+	int err;
+
+	err = mnl_attr_parse_nested(nl, attr_cb, nla_match_value);
+	if (err != MNL_CB_OK)
+		return -EINVAL;
+
+	if (!nla_match_value[DEVLINK_ATTR_DPIPE_MATCH] ||
+	    !nla_match_value[DEVLINK_ATTR_DPIPE_VALUE]) {
+		return -EINVAL;
+	}
+
+	pr_out_entry_start(ctx->dl);
+	if (dpipe_match_show(ctx, nla_match_value[DEVLINK_ATTR_DPIPE_MATCH]))
+		goto err_match_show;
+	if (dpipe_entry_value_show(ctx, nla_match_value))
+		goto err_value_show;
+	pr_out_entry_end(ctx->dl);
+
+	return 0;
+
+err_match_show:
+err_value_show:
+	pr_out_entry_end(ctx->dl);
+	return -EINVAL;
+}
+
+static int dpipe_entry_action_value_show(struct dpipe_ctx *ctx,
+					 struct nlattr *nl)
+{
+	struct nlattr *nla_action_value[DEVLINK_ATTR_MAX + 1] = {};
+	int err;
+
+	err = mnl_attr_parse_nested(nl, attr_cb, nla_action_value);
+	if (err != MNL_CB_OK)
+		return -EINVAL;
+
+	if (!nla_action_value[DEVLINK_ATTR_DPIPE_ACTION] ||
+	    !nla_action_value[DEVLINK_ATTR_DPIPE_VALUE]) {
+		return -EINVAL;
+	}
+
+	pr_out_entry_start(ctx->dl);
+	if (dpipe_action_show(ctx, nla_action_value[DEVLINK_ATTR_DPIPE_ACTION]))
+		goto err_action_show;
+	if (dpipe_entry_value_show(ctx, nla_action_value))
+		goto err_value_show;
+	pr_out_entry_end(ctx->dl);
+
+	return 0;
+
+err_action_show:
+err_value_show:
+	pr_out_entry_end(ctx->dl);
+	return -EINVAL;
+}
+
+static int
+dpipe_tables_action_values_show(struct dpipe_ctx *ctx,
+				struct nlattr *nla_action_values)
+{
+	struct nlattr *nla_action_value;
+
+	mnl_attr_for_each_nested(nla_action_value, nla_action_values) {
+		if (dpipe_entry_action_value_show(ctx, nla_action_value))
+			return -EINVAL;
+	}
+	return 0;
+}
+
+static int
+dpipe_tables_match_values_show(struct dpipe_ctx *ctx,
+			       struct nlattr *nla_match_values)
+{
+	struct nlattr *nla_match_value;
+
+	mnl_attr_for_each_nested(nla_match_value, nla_match_values) {
+		if (dpipe_entry_match_value_show(ctx, nla_match_value))
+			return -EINVAL;
+	}
+	return 0;
+}
+
+static int dpipe_entry_show(struct dpipe_ctx *ctx, struct nlattr *nl)
+{
+	struct nlattr *nla_entry[DEVLINK_ATTR_MAX + 1] = {};
+	uint32_t entry_index;
+	uint64_t counter;
+	int err;
+
+	err = mnl_attr_parse_nested(nl, attr_cb, nla_entry);
+	if (err != MNL_CB_OK)
+		return -EINVAL;
+
+	if (!nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_INDEX] ||
+	    !nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_MATCH_VALUES] ||
+	    !nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_ACTION_VALUES]) {
+		return -EINVAL;
+	}
+
+	entry_index = mnl_attr_get_u32(nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_INDEX]);
+	pr_out_uint(ctx->dl, "index", entry_index);
+
+	if (nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_COUNTER]) {
+		counter = mnl_attr_get_u64(nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_COUNTER]);
+		pr_out_uint(ctx->dl, "counter", counter);
+	}
+
+	pr_out_array_start(ctx->dl, "match_value");
+	if (dpipe_tables_match_values_show(ctx,
+					   nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_MATCH_VALUES]))
+		goto err_match_values_show;
+	pr_out_array_end(ctx->dl);
+
+	pr_out_array_start(ctx->dl, "action_value");
+	if (dpipe_tables_action_values_show(ctx,
+					    nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_ACTION_VALUES]))
+		goto err_action_values_show;
+	pr_out_array_end(ctx->dl);
+	return 0;
+
+err_action_values_show:
+err_match_values_show:
+	pr_out_array_end(ctx->dl);
+	return -EINVAL;
+}
+
+static int dpipe_table_entries_show(struct dpipe_ctx *ctx, struct nlattr **tb)
+{
+	struct nlattr *nla_entries = tb[DEVLINK_ATTR_DPIPE_ENTRIES];
+	struct nlattr *nla_entry;
+
+	mnl_attr_for_each_nested(nla_entry, nla_entries) {
+		pr_out_handle_start_arr(ctx->dl, tb);
+		if (dpipe_entry_show(ctx, nla_entry))
+			goto err_entry_show;
+		pr_out_handle_end(ctx->dl);
+	}
+	return 0;
+
+err_entry_show:
+	pr_out_handle_end(ctx->dl);
+	return -EINVAL;
+}
+
+static int cmd_dpipe_table_entry_dump_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct dpipe_ctx *ctx = data;
+	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
+	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+
+	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
+	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
+	    !tb[DEVLINK_ATTR_DPIPE_ENTRIES])
+		return MNL_CB_ERROR;
+
+	if (dpipe_table_entries_show(ctx, tb))
+		return MNL_CB_ERROR;
+	return MNL_CB_OK;
+}
+
+static int cmd_dpipe_table_dump(struct dl *dl)
+{
+	struct nlmsghdr *nlh;
+	struct dpipe_ctx *ctx;
+	uint16_t flags = NLM_F_REQUEST;
+	int err;
+
+	ctx = dpipe_ctx_alloc(dl);
+	if (!ctx)
+		return -ENOMEM;
+
+	err = dl_argv_parse(dl, DL_OPT_HANDLE | DL_OPT_DPIPE_TABLE_NAME, 0);
+	if (err)
+		goto out;
+
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_HEADERS_GET, flags);
+	dl_opts_put(nlh, dl);
+	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_header_cb, ctx);
+	if (err) {
+		pr_err("error get headers %s\n", strerror(ctx->err));
+		goto out;
+	}
+
+	flags = NLM_F_REQUEST | NLM_F_ACK;
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_ENTRIES_GET, flags);
+	dl_opts_put(nlh, dl);
+
+	pr_out_section_start(dl, "table_entry");
+	_mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_table_entry_dump_cb, ctx);
+	pr_out_section_end(dl);
+out:
+	dpipe_ctx_clear(ctx);
+	dpipe_ctx_free(ctx);
+	return err;
+}
+
+static void cmd_dpipe_table_help(void)
+{
+	pr_err("Usage: devlink dpipe table [ OBJECT-LIST ]\n"
+	       "where  OBJECT-LIST := { show | set | dump }\n");
+}
+
+static int cmd_dpipe_table(struct dl *dl)
+{
+	if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
+		cmd_dpipe_table_help();
+		return 0;
+	} else if (dl_argv_match(dl, "show")) {
+		dl_arg_inc(dl);
+		return cmd_dpipe_table_show(dl);
+	} else if (dl_argv_match(dl, "set")) {
+		dl_arg_inc(dl);
+		return cmd_dpipe_table_set(dl);
+	}  else if (dl_argv_match(dl, "dump")) {
+		dl_arg_inc(dl);
+		return cmd_dpipe_table_dump(dl);
+	}
+	pr_err("Command \"%s\" not found\n", dl_argv(dl));
+	return -ENOENT;
+}
+
+static void cmd_dpipe_help(void)
+{
+	pr_err("Usage: devlink dpipe [ OBJECT-LIST ]\n"
+	       "where  OBJECT-LIST := { header | table }\n");
+}
+
+static int cmd_dpipe(struct dl *dl)
+{
+	if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
+		cmd_dpipe_help();
+		return 0;
+	} else if (dl_argv_match(dl, "header")) {
+		dl_arg_inc(dl);
+		return cmd_dpipe_header(dl);
+	} else if (dl_argv_match(dl, "table")) {
+		dl_arg_inc(dl);
+		return cmd_dpipe_table(dl);
+	}
+	pr_err("Command \"%s\" not found\n", dl_argv(dl));
+	return -ENOENT;
+}
+
+static void help(void)
+{
+	pr_err("Usage: devlink [ OPTIONS ] OBJECT { COMMAND | help }\n"
+	       "where  OBJECT := { dev | port | sb | monitor | dpipe }\n"
+	       "       OPTIONS := { -V[ersion] | -n[no-nice-names] | -j[json] | -p[pretty] | -v[verbose] }\n");
+}
+
+static int dl_cmd(struct dl *dl)
+{
+	if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
+		help();
+		return 0;
+	} else if (dl_argv_match(dl, "dev")) {
+		dl_arg_inc(dl);
+		return cmd_dev(dl);
+	} else if (dl_argv_match(dl, "port")) {
+		dl_arg_inc(dl);
+		return cmd_port(dl);
+	} else if (dl_argv_match(dl, "sb")) {
+		dl_arg_inc(dl);
+		return cmd_sb(dl);
+	} else if (dl_argv_match(dl, "monitor")) {
+		dl_arg_inc(dl);
+		return cmd_mon(dl);
+	} else if (dl_argv_match(dl, "dpipe")) {
+		dl_arg_inc(dl);
+		return cmd_dpipe(dl);
+	}
+	pr_err("Object \"%s\" not found\n", dl_argv(dl));
+	return -ENOENT;
+}
+
+static int dl_init(struct dl *dl, int argc, char **argv)
+{
+	int err;
+
+	dl->argc = argc;
+	dl->argv = argv;
+
+	dl->nlg = mnlg_socket_open(DEVLINK_GENL_NAME, DEVLINK_GENL_VERSION);
+	if (!dl->nlg) {
+		pr_err("Failed to connect to devlink Netlink\n");
+		return -errno;
+	}
+
+	err = ifname_map_init(dl);
+	if (err) {
+		pr_err("Failed to create index map\n");
+		goto err_ifname_map_create;
+	}
+	if (dl->json_output) {
+		dl->jw = jsonw_new(stdout);
+		if (!dl->jw) {
+			pr_err("Failed to create JSON writer\n");
+			goto err_json_new;
+		}
+		jsonw_pretty(dl->jw, dl->pretty_output);
+	}
+	return 0;
+
+err_json_new:
+	ifname_map_fini(dl);
+err_ifname_map_create:
+	mnlg_socket_close(dl->nlg);
+	return err;
+}
+
+static void dl_fini(struct dl *dl)
+{
+	if (dl->json_output)
+		jsonw_destroy(&dl->jw);
+	ifname_map_fini(dl);
+	mnlg_socket_close(dl->nlg);
+}
+
+static struct dl *dl_alloc(void)
+{
+	struct dl *dl;
+
+	dl = calloc(1, sizeof(*dl));
+	if (!dl)
+		return NULL;
+	return dl;
+}
+
+static void dl_free(struct dl *dl)
+{
+	free(dl);
+}
+
+int main(int argc, char **argv)
+{
+	static const struct option long_options[] = {
+		{ "Version",		no_argument,		NULL, 'V' },
+		{ "no-nice-names",	no_argument,		NULL, 'n' },
+		{ "json",		no_argument,		NULL, 'j' },
+		{ "pretty",		no_argument,		NULL, 'p' },
+		{ "verbose",		no_argument,		NULL, 'v' },
+		{ NULL, 0, NULL, 0 }
+	};
+	struct dl *dl;
+	int opt;
+	int err;
+	int ret;
+
+	dl = dl_alloc();
+	if (!dl) {
+		pr_err("Failed to allocate memory for devlink\n");
+		return EXIT_FAILURE;
+	}
+
+	while ((opt = getopt_long(argc, argv, "Vnjpv",
+				  long_options, NULL)) >= 0) {
+
+		switch (opt) {
+		case 'V':
+			printf("devlink utility, iproute2-ss%s\n", SNAPSHOT);
+			ret = EXIT_SUCCESS;
+			goto dl_free;
+		case 'n':
+			dl->no_nice_names = true;
+			break;
+		case 'j':
 			dl->json_output = true;
 			break;
 		case 'p':
 			dl->pretty_output = true;
 			break;
+		case 'v':
+			dl->verbose = true;
+			break;
 		default:
 			pr_err("Unknown option.\n");
 			help();
diff --git a/include/linux/devlink.h b/include/linux/devlink.h
index 2ad3585..0c8af61 100644
--- a/include/linux/devlink.h
+++ b/include/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_TABLE_GET,
+	DEVLINK_CMD_DPIPE_ENTRIES_GET,
+	DEVLINK_CMD_DPIPE_HEADERS_GET,
+	DEVLINK_CMD_DPIPE_TABLE_COUNTERS_SET,
 
+	/* add new commands above here */
 	__DEVLINK_CMD_MAX,
 	DEVLINK_CMD_MAX = __DEVLINK_CMD_MAX - 1
 };
@@ -148,10 +152,71 @@ 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,		/* u64 */
+	DEVLINK_ATTR_DPIPE_TABLE_MATCHES,	/* nested */
+	DEVLINK_ATTR_DPIPE_TABLE_ACTIONS,	/* nested */
+	DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED,	/* u8 */
+
+	DEVLINK_ATTR_DPIPE_ENTRIES,		/* nested */
+	DEVLINK_ATTR_DPIPE_ENTRY,		/* nested */
+	DEVLINK_ATTR_DPIPE_ENTRY_INDEX,		/* u64 */
+	DEVLINK_ATTR_DPIPE_ENTRY_MATCH_VALUES,	/* nested */
+	DEVLINK_ATTR_DPIPE_ENTRY_ACTION_VALUES,	/* nested */
+	DEVLINK_ATTR_DPIPE_ENTRY_COUNTER,	/* u64 */
+
+	DEVLINK_ATTR_DPIPE_MATCH,		/* nested */
+	DEVLINK_ATTR_DPIPE_MATCH_VALUE,		/* nested */
+	DEVLINK_ATTR_DPIPE_MATCH_TYPE,		/* u32 */
+
+	DEVLINK_ATTR_DPIPE_ACTION,		/* nested */
+	DEVLINK_ATTR_DPIPE_ACTION_VALUE,	/* nested */
+	DEVLINK_ATTR_DPIPE_ACTION_TYPE,		/* u32 */
+
+	DEVLINK_ATTR_DPIPE_VALUE,
+	DEVLINK_ATTR_DPIPE_VALUE_MASK,
+	DEVLINK_ATTR_DPIPE_VALUE_MAPPING,	/* 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,	/* nested */
+	DEVLINK_ATTR_DPIPE_HEADER_GLOBAL,	/* u8 */
+	DEVLINK_ATTR_DPIPE_HEADER_INDEX,	/* u32 */
+
+	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,
+};
+
+/* Match type - specify the type of the match */
+enum devlink_dpipe_match_type {
+	DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT,
+};
+
+/* Action type - specify the action type */
+enum devlink_dpipe_action_type {
+	DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY,
+};
+
 #endif /* _LINUX_DEVLINK_H_ */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [patch net-next 0/8] Add support for pipeline debug (dpipe)
  2017-03-25  7:35 [patch net-next 0/8] Add support for pipeline debug (dpipe) Jiri Pirko
                   ` (8 preceding siblings ...)
  2017-03-25  7:37 ` [patch iproute2/net-next] devlink: Add support for pipeline debug (dpipe) Jiri Pirko
@ 2017-03-27 22:48 ` David Miller
  2017-03-28  6:51   ` Jiri Pirko
  2017-03-28  7:10   ` Jiri Pirko
  9 siblings, 2 replies; 20+ messages in thread
From: David Miller @ 2017-03-27 22:48 UTC (permalink / raw)
  To: jiri
  Cc: netdev, arkadis, idosch, mlxsw, jhs, ivecera, roopa, f.fainelli,
	vivien.didelot, john.fastabend, andrew, simon.horman


Please fix up these warnings and resubmit:

drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c: In function ‘mlxsw_sp_rif_counter_free’:
drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c:208:2: warning: ‘p_counter_index’ may be used uninitialized in this function [-Wmaybe-uninitialized]
  mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
  ^
drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c: In function ‘mlxsw_sp_table_erif_entries_dump’:
drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c:220:9: warning: missing braces around initializer [-Wmissing-braces]
  struct devlink_dpipe_value match_value = {0}, action_value = {0};
         ^
drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c:220:9: warning: (near initialization for ‘match_value.<anonymous>’) [-Wmissing-braces]
drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c:220:9: warning: missing braces around initializer [-Wmissing-braces]
drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c:220:9: warning: (near initialization for ‘action_value.<anonymous>’) [-Wmissing-braces]

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [patch net-next 0/8] Add support for pipeline debug (dpipe)
  2017-03-27 22:48 ` [patch net-next 0/8] " David Miller
@ 2017-03-28  6:51   ` Jiri Pirko
  2017-03-28  7:58     ` Ivan Vecera
  2017-03-28 18:00     ` David Miller
  2017-03-28  7:10   ` Jiri Pirko
  1 sibling, 2 replies; 20+ messages in thread
From: Jiri Pirko @ 2017-03-28  6:51 UTC (permalink / raw)
  To: David Miller
  Cc: netdev, arkadis, idosch, mlxsw, jhs, ivecera, roopa, f.fainelli,
	vivien.didelot, john.fastabend, andrew, simon.horman

Tue, Mar 28, 2017 at 12:48:34AM CEST, davem@davemloft.net wrote:
>
>Please fix up these warnings and resubmit:
>
>drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c: In function ‘mlxsw_sp_rif_counter_free’:
>drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c:208:2: warning: ‘p_counter_index’ may be used uninitialized in this function [-Wmaybe-uninitialized]
>  mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
>  ^
>drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c: In function ‘mlxsw_sp_table_erif_entries_dump’:
>drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c:220:9: warning: missing braces around initializer [-Wmissing-braces]
>  struct devlink_dpipe_value match_value = {0}, action_value = {0};

I know about this warning. I believe that it is a gcc bug. It happens
for gcc 4, but gcc 6 is silent, therefore I choose to ignore this. Do
you still want me to fix it?


>         ^
>drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c:220:9: warning: (near initialization for ‘match_value.<anonymous>’) [-Wmissing-braces]
>drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c:220:9: warning: missing braces around initializer [-Wmissing-braces]
>drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c:220:9: warning: (near initialization for ‘action_value.<anonymous>’) [-Wmissing-braces]

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [patch net-next 0/8] Add support for pipeline debug (dpipe)
  2017-03-27 22:48 ` [patch net-next 0/8] " David Miller
  2017-03-28  6:51   ` Jiri Pirko
@ 2017-03-28  7:10   ` Jiri Pirko
  2017-03-28  7:42     ` Ivan Vecera
  1 sibling, 1 reply; 20+ messages in thread
From: Jiri Pirko @ 2017-03-28  7:10 UTC (permalink / raw)
  To: David Miller
  Cc: netdev, arkadis, idosch, mlxsw, jhs, ivecera, roopa, f.fainelli,
	vivien.didelot, john.fastabend, andrew, simon.horman

Tue, Mar 28, 2017 at 12:48:34AM CEST, davem@davemloft.net wrote:
>
>Please fix up these warnings and resubmit:
>
>drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c: In function ‘mlxsw_sp_rif_counter_free’:
>drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c:208:2: warning: ‘p_counter_index’ may be used uninitialized in this function [-Wmaybe-uninitialized]
>  mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
>  ^

This is I believe also gcc bug. Code looks fine. I'm not getting the warning
with gcc 6

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [patch net-next 0/8] Add support for pipeline debug (dpipe)
  2017-03-28  7:10   ` Jiri Pirko
@ 2017-03-28  7:42     ` Ivan Vecera
  2017-03-28  7:57       ` Jiri Pirko
  0 siblings, 1 reply; 20+ messages in thread
From: Ivan Vecera @ 2017-03-28  7:42 UTC (permalink / raw)
  To: Jiri Pirko, David Miller
  Cc: netdev, arkadis, idosch, mlxsw, jhs, roopa, f.fainelli,
	vivien.didelot, john.fastabend, andrew, simon.horman

Dne 28.3.2017 v 09:10 Jiri Pirko napsal(a):
> Tue, Mar 28, 2017 at 12:48:34AM CEST, davem@davemloft.net wrote:
>>
>> Please fix up these warnings and resubmit:
>>
>> drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c: In function ‘mlxsw_sp_rif_counter_free’:
>> drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c:208:2: warning: ‘p_counter_index’ may be used uninitialized in this function [-Wmaybe-uninitialized]
>>  mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
>>  ^
>
> This is I believe also gcc bug. Code looks fine. I'm not getting the warning
> with gcc 6
>
No it is not a gcc bug. The function mlxsw_sp_rif_counter_free() is not static 
so the compiler cannot know all its callers and so 'dir' parameter can be 
theoretically anything.
You call mlxsw_sp_rif_p_counter_get() there it assumes dir can be only 
MLXSW_SP_RIF_COUNTER_EGRESS or MLXSW_SP_RIF_COUNTER_INGRESS so initializes 
*pp_counter_index only for them. For any other value the value is uninitialized.

Ivan

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [patch net-next 0/8] Add support for pipeline debug (dpipe)
  2017-03-28  7:42     ` Ivan Vecera
@ 2017-03-28  7:57       ` Jiri Pirko
  2017-03-28  7:59         ` Jiri Pirko
  0 siblings, 1 reply; 20+ messages in thread
From: Jiri Pirko @ 2017-03-28  7:57 UTC (permalink / raw)
  To: Ivan Vecera
  Cc: David Miller, netdev, arkadis, idosch, mlxsw, jhs, roopa,
	f.fainelli, vivien.didelot, john.fastabend, andrew, simon.horman

Tue, Mar 28, 2017 at 09:42:58AM CEST, ivecera@redhat.com wrote:
>Dne 28.3.2017 v 09:10 Jiri Pirko napsal(a):
>> Tue, Mar 28, 2017 at 12:48:34AM CEST, davem@davemloft.net wrote:
>> > 
>> > Please fix up these warnings and resubmit:
>> > 
>> > drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c: In function ‘mlxsw_sp_rif_counter_free’:
>> > drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c:208:2: warning: ‘p_counter_index’ may be used uninitialized in this function [-Wmaybe-uninitialized]
>> >  mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
>> >  ^
>> 
>> This is I believe also gcc bug. Code looks fine. I'm not getting the warning
>> with gcc 6
>> 
>No it is not a gcc bug. The function mlxsw_sp_rif_counter_free() is not
>static so the compiler cannot know all its callers and so 'dir' parameter can
>be theoretically anything.
>You call mlxsw_sp_rif_p_counter_get() there it assumes dir can be only
>MLXSW_SP_RIF_COUNTER_EGRESS or MLXSW_SP_RIF_COUNTER_INGRESS so initializes
>*pp_counter_index only for them. For any other value the value is
>uninitialized.

Interesting, why gcc 6.2.1 is silent then?

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [patch net-next 0/8] Add support for pipeline debug (dpipe)
  2017-03-28  6:51   ` Jiri Pirko
@ 2017-03-28  7:58     ` Ivan Vecera
  2017-03-28  8:05       ` Jiri Pirko
  2017-03-28 18:00     ` David Miller
  1 sibling, 1 reply; 20+ messages in thread
From: Ivan Vecera @ 2017-03-28  7:58 UTC (permalink / raw)
  To: Jiri Pirko, David Miller
  Cc: netdev, arkadis, idosch, mlxsw, jhs, roopa, f.fainelli,
	vivien.didelot, john.fastabend, andrew, simon.horman

Dne 28.3.2017 v 08:51 Jiri Pirko napsal(a):
> Tue, Mar 28, 2017 at 12:48:34AM CEST, davem@davemloft.net wrote:
>>
>> Please fix up these warnings and resubmit:
>>
>> drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c: In function ‘mlxsw_sp_rif_counter_free’:
>> drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c:208:2: warning: ‘p_counter_index’ may be used uninitialized in this function [-Wmaybe-uninitialized]
>>  mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
>>  ^
>> drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c: In function ‘mlxsw_sp_table_erif_entries_dump’:
>> drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c:220:9: warning: missing braces around initializer [-Wmissing-braces]
>>  struct devlink_dpipe_value match_value = {0}, action_value = {0};
>
> I know about this warning. I believe that it is a gcc bug. It happens
> for gcc 4, but gcc 6 is silent, therefore I choose to ignore this. Do
> you still want me to fix it?
>
Yes, this is gcc bug (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53119). The 
problem is the first member of struct is union so older gcc assumes initializer 
like '{ { 0 } }' instead of '{ 0 }'.

IMHO it should be better to workaround this bug for older gccs as gcc6 is not 
only supported compiler.

Ivan

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [patch net-next 0/8] Add support for pipeline debug (dpipe)
  2017-03-28  7:57       ` Jiri Pirko
@ 2017-03-28  7:59         ` Jiri Pirko
  2017-03-28  8:10           ` Ivan Vecera
  0 siblings, 1 reply; 20+ messages in thread
From: Jiri Pirko @ 2017-03-28  7:59 UTC (permalink / raw)
  To: Ivan Vecera
  Cc: David Miller, netdev, arkadis, idosch, mlxsw, jhs, roopa,
	f.fainelli, vivien.didelot, john.fastabend, andrew, simon.horman

Tue, Mar 28, 2017 at 09:57:22AM CEST, jiri@resnulli.us wrote:
>Tue, Mar 28, 2017 at 09:42:58AM CEST, ivecera@redhat.com wrote:
>>Dne 28.3.2017 v 09:10 Jiri Pirko napsal(a):
>>> Tue, Mar 28, 2017 at 12:48:34AM CEST, davem@davemloft.net wrote:
>>> > 
>>> > Please fix up these warnings and resubmit:
>>> > 
>>> > drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c: In function ‘mlxsw_sp_rif_counter_free’:
>>> > drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c:208:2: warning: ‘p_counter_index’ may be used uninitialized in this function [-Wmaybe-uninitialized]
>>> >  mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
>>> >  ^
>>> 
>>> This is I believe also gcc bug. Code looks fine. I'm not getting the warning
>>> with gcc 6
>>> 
>>No it is not a gcc bug. The function mlxsw_sp_rif_counter_free() is not
>>static so the compiler cannot know all its callers and so 'dir' parameter can
>>be theoretically anything.
>>You call mlxsw_sp_rif_p_counter_get() there it assumes dir can be only
>>MLXSW_SP_RIF_COUNTER_EGRESS or MLXSW_SP_RIF_COUNTER_INGRESS so initializes
>>*pp_counter_index only for them. For any other value the value is
>>uninitialized.
>
>Interesting, why gcc 6.2.1 is silent then?

Oh, I see that it is not :) My bad, will fix, thanks!

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [patch net-next 0/8] Add support for pipeline debug (dpipe)
  2017-03-28  7:58     ` Ivan Vecera
@ 2017-03-28  8:05       ` Jiri Pirko
  0 siblings, 0 replies; 20+ messages in thread
From: Jiri Pirko @ 2017-03-28  8:05 UTC (permalink / raw)
  To: Ivan Vecera
  Cc: David Miller, netdev, arkadis, idosch, mlxsw, jhs, roopa,
	f.fainelli, vivien.didelot, john.fastabend, andrew, simon.horman

Tue, Mar 28, 2017 at 09:58:57AM CEST, ivecera@redhat.com wrote:
>Dne 28.3.2017 v 08:51 Jiri Pirko napsal(a):
>> Tue, Mar 28, 2017 at 12:48:34AM CEST, davem@davemloft.net wrote:
>> > 
>> > Please fix up these warnings and resubmit:
>> > 
>> > drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c: In function ‘mlxsw_sp_rif_counter_free’:
>> > drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c:208:2: warning: ‘p_counter_index’ may be used uninitialized in this function [-Wmaybe-uninitialized]
>> >  mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
>> >  ^
>> > drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c: In function ‘mlxsw_sp_table_erif_entries_dump’:
>> > drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c:220:9: warning: missing braces around initializer [-Wmissing-braces]
>> >  struct devlink_dpipe_value match_value = {0}, action_value = {0};
>> 
>> I know about this warning. I believe that it is a gcc bug. It happens
>> for gcc 4, but gcc 6 is silent, therefore I choose to ignore this. Do
>> you still want me to fix it?
>> 
>Yes, this is gcc bug (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53119).
>The problem is the first member of struct is union so older gcc assumes
>initializer like '{ { 0 } }' instead of '{ 0 }'.
>
>IMHO it should be better to workaround this bug for older gccs as gcc6 is not
>only supported compiler.

Okay. Thanks!

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [patch net-next 0/8] Add support for pipeline debug (dpipe)
  2017-03-28  7:59         ` Jiri Pirko
@ 2017-03-28  8:10           ` Ivan Vecera
  0 siblings, 0 replies; 20+ messages in thread
From: Ivan Vecera @ 2017-03-28  8:10 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: David Miller, netdev, arkadis, idosch, mlxsw, jhs, roopa,
	f.fainelli, vivien.didelot, john.fastabend, andrew, simon.horman

Dne 28.3.2017 v 09:59 Jiri Pirko napsal(a):
> Tue, Mar 28, 2017 at 09:57:22AM CEST, jiri@resnulli.us wrote:
>> Tue, Mar 28, 2017 at 09:42:58AM CEST, ivecera@redhat.com wrote:
>>> Dne 28.3.2017 v 09:10 Jiri Pirko napsal(a):
>>>> Tue, Mar 28, 2017 at 12:48:34AM CEST, davem@davemloft.net wrote:
>>>>>
>>>>> Please fix up these warnings and resubmit:
>>>>>
>>>>> drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c: In function ‘mlxsw_sp_rif_counter_free’:
>>>>> drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c:208:2: warning: ‘p_counter_index’ may be used uninitialized in this function [-Wmaybe-uninitialized]
>>>>>  mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
>>>>>  ^
>>>>
>>>> This is I believe also gcc bug. Code looks fine. I'm not getting the warning
>>>> with gcc 6
>>>>
>>> No it is not a gcc bug. The function mlxsw_sp_rif_counter_free() is not
>>> static so the compiler cannot know all its callers and so 'dir' parameter can
>>> be theoretically anything.
>>> You call mlxsw_sp_rif_p_counter_get() there it assumes dir can be only
>>> MLXSW_SP_RIF_COUNTER_EGRESS or MLXSW_SP_RIF_COUNTER_INGRESS so initializes
>>> *pp_counter_index only for them. For any other value the value is
>>> uninitialized.
>>
>> Interesting, why gcc 6.2.1 is silent then?
>
> Oh, I see that it is not :) My bad, will fix, thanks!
>
Probably because 'dir' is enumeration and this gcc assumes that any caller uses 
only defined enum values.

Ivan

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [patch net-next 0/8] Add support for pipeline debug (dpipe)
  2017-03-28  6:51   ` Jiri Pirko
  2017-03-28  7:58     ` Ivan Vecera
@ 2017-03-28 18:00     ` David Miller
  1 sibling, 0 replies; 20+ messages in thread
From: David Miller @ 2017-03-28 18:00 UTC (permalink / raw)
  To: jiri
  Cc: netdev, arkadis, idosch, mlxsw, jhs, ivecera, roopa, f.fainelli,
	vivien.didelot, john.fastabend, andrew, simon.horman

From: Jiri Pirko <jiri@resnulli.us>
Date: Tue, 28 Mar 2017 08:51:11 +0200

> Tue, Mar 28, 2017 at 12:48:34AM CEST, davem@davemloft.net wrote:
>>
>>Please fix up these warnings and resubmit:
>>
>>drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c: In function ‘mlxsw_sp_rif_counter_free’:
>>drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c:208:2: warning: ‘p_counter_index’ may be used uninitialized in this function [-Wmaybe-uninitialized]
>>  mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index,
>>  ^
>>drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c: In function ‘mlxsw_sp_table_erif_entries_dump’:
>>drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c:220:9: warning: missing braces around initializer [-Wmissing-braces]
>>  struct devlink_dpipe_value match_value = {0}, action_value = {0};
> 
> I know about this warning. I believe that it is a gcc bug. It happens
> for gcc 4, but gcc 6 is silent, therefore I choose to ignore this. Do
> you still want me to fix it?

Please do.

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2017-03-28 18:00 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-03-25  7:35 [patch net-next 0/8] Add support for pipeline debug (dpipe) Jiri Pirko
2017-03-25  7:35 ` [patch net-next 1/8] devlink: Support " Jiri Pirko
2017-03-25  7:35 ` [patch net-next 2/8] mlxsw: reg: Add counter fields to RITR register Jiri Pirko
2017-03-25  7:35 ` [patch net-next 3/8] mlxsw: spectrum: Add placeholder for dpipe Jiri Pirko
2017-03-25  7:35 ` [patch net-next 4/8] mlxsw: spectrum: Add definition for egress rif table Jiri Pirko
2017-03-25  7:35 ` [patch net-next 5/8] mlxsw: reg: Add Router Interface Counter Register Jiri Pirko
2017-03-25  7:35 ` [patch net-next 6/8] mlxsw: spectrum: Support for counters on router interfaces Jiri Pirko
2017-03-25  7:35 ` [patch net-next 7/8] mlxsw: spectrum_router: Add rif helper functions Jiri Pirko
2017-03-25  7:35 ` [patch net-next 8/8] mlxsw: spectrum: Add Support for erif table entries access Jiri Pirko
2017-03-25  7:37 ` [patch iproute2/net-next] devlink: Add support for pipeline debug (dpipe) Jiri Pirko
2017-03-27 22:48 ` [patch net-next 0/8] " David Miller
2017-03-28  6:51   ` Jiri Pirko
2017-03-28  7:58     ` Ivan Vecera
2017-03-28  8:05       ` Jiri Pirko
2017-03-28 18:00     ` David Miller
2017-03-28  7:10   ` Jiri Pirko
2017-03-28  7:42     ` Ivan Vecera
2017-03-28  7:57       ` Jiri Pirko
2017-03-28  7:59         ` Jiri Pirko
2017-03-28  8:10           ` Ivan Vecera

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.