naccyde / rpms / iproute

Forked from rpms/iproute 9 months ago
Clone

Blame SOURCES/0026-devlink-Add-support-for-pipeline-debug-dpipe.patch

36cfb7
From 120f9c488ba7d291f899f1ec2f77e0ae33efcd88 Mon Sep 17 00:00:00 2001
36cfb7
From: Kamal Heib <kheib@redhat.com>
36cfb7
Date: Thu, 9 Nov 2017 04:44:32 -0500
36cfb7
Subject: [PATCH] devlink: Add support for pipeline debug (dpipe)
36cfb7
36cfb7
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1456539
36cfb7
36cfb7
commit 153c1a9b21e5b7b78e066de2b93a4edb8c3dc498
36cfb7
Author: Arkadi Sharshevsky <arkadis@mellanox.com>
36cfb7
Date:   Wed May 3 13:25:23 2017 +0200
36cfb7
36cfb7
    devlink: Add support for pipeline debug (dpipe)
36cfb7
36cfb7
    Add support for pipeline debug (dpipe). The headers are used both the
36cfb7
    gain visibillity into the headers supported by the hardware, and to
36cfb7
    build the headers/field database which is used by other commands.
36cfb7
36cfb7
    Examples:
36cfb7
36cfb7
    First we can see the headers supported by the hardware:
36cfb7
36cfb7
    $devlink dpipe header show pci/0000:03:00.0
36cfb7
36cfb7
    pci/0000:03:00.0:
36cfb7
      name mlxsw_meta
36cfb7
      field:
36cfb7
        name erif_port bitwidth 32 mapping_type ifindex
36cfb7
        name l3_forward bitwidth 1
36cfb7
        name l3_drop bitwidth 1
36cfb7
36cfb7
    Note that mapping_type is presented only if relevant. Also the header/
36cfb7
    field id's are reported by the kernel they are not shown by default.
36cfb7
    They can be observed by using the -v option. Also the headers scope
36cfb7
    (global/local) is specified.
36cfb7
36cfb7
    $devlink -v dpipe header show pci/0000:03:00.0
36cfb7
36cfb7
    pci/0000:03:00.0:
36cfb7
      name mlxsw_meta id 0 global false
36cfb7
      field:
36cfb7
        name erif_port id 0 bitwidth 32 mapping_type ifindex
36cfb7
        name l3_forward id 1 bitwidth 1
36cfb7
        name l3_drop id 2 bitwidth 1
36cfb7
36cfb7
    Second we can examine the tables supported by the hardware. In order
36cfb7
    to dump all the tables no table name should be provided:
36cfb7
    $devlink dpipe table show pci/0000:03:00.0
36cfb7
36cfb7
    In order to examine specific table its name have to be specified:
36cfb7
    $devlink dpipe table show pci/0000:03:00.0 name erif
36cfb7
36cfb7
    pci/0000:03:00.0:
36cfb7
      name mlxsw_erif size 800 counters_enabled true
36cfb7
      match:
36cfb7
        type field_exact header mlxsw_meta field erif_port mapping ifindex
36cfb7
      action:
36cfb7
        type field_modify header mlxsw_meta field l3_forward
36cfb7
        type field_modify header mlxsw_meta field l3_drop
36cfb7
36cfb7
    To enable/disable counters on the table:
36cfb7
    $devlink dpipe table set pci/0000:03:00.0 name erif counters enable
36cfb7
    $devlink dpipe table set pci/0000:03:00.0 name erif counters disable
36cfb7
36cfb7
    In order to see the current entries in the hardware for specific table:
36cfb7
    $devlink dpipe table dump pci/0000:03:00.0 name erif
36cfb7
36cfb7
    pci/0000:03:00.0:
36cfb7
      index 0 counter 0
36cfb7
      match_value:
36cfb7
        type field_exact header mlxsw_meta field erif_port mapping ifindex mapping_value 383 value 0
36cfb7
      action_value:
36cfb7
        type field_modify header mlxsw_meta field l3_forward value 1
36cfb7
36cfb7
      index 1 counter 0
36cfb7
      match_value:
36cfb7
        type field_exact header mlxsw_meta field erif_port mapping ifindex mapping_value 381 value 1
36cfb7
      action_value:
36cfb7
        type field_modify header mlxsw_meta field l3_forward value 1
36cfb7
36cfb7
    In the above example the table contains two entries which does match
36cfb7
    on erif port and forwards the packet or drop it (currently only the
36cfb7
    forward count is implemented). The counter values are provided for
36cfb7
    example. In case the counting is not enabled on the table the counters
36cfb7
    will not be available.
36cfb7
36cfb7
    Signed-off-by: Arkadi Sharshevsky <arkadis@mellanox.com>
36cfb7
    Signed-off-by: Jiri Pirko <jiri@mellanox.com>
36cfb7
36cfb7
Signed-off-by: Kamal Heib <kheib@redhat.com>
36cfb7
---
36cfb7
 devlink/devlink.c | 1353 +++++++++++++++++++++++++++++++++++++++++++++++++----
36cfb7
 1 file changed, 1254 insertions(+), 99 deletions(-)
36cfb7
36cfb7
diff --git a/devlink/devlink.c b/devlink/devlink.c
36cfb7
index 35220d8..e22ee0a 100644
36cfb7
--- a/devlink/devlink.c
36cfb7
+++ b/devlink/devlink.c
36cfb7
@@ -34,7 +34,15 @@
36cfb7
 #define ESWITCH_INLINE_MODE_TRANSPORT "transport"
36cfb7
 
36cfb7
 #define pr_err(args...) fprintf(stderr, ##args)
36cfb7
-#define pr_out(args...) fprintf(stdout, ##args)
36cfb7
+#define pr_out(args...)						\
36cfb7
+	do {							\
36cfb7
+		if (g_indent_newline) {				\
36cfb7
+			fprintf(stdout, "%s", g_indent_str);	\
36cfb7
+			g_indent_newline = false;		\
36cfb7
+		}						\
36cfb7
+		fprintf(stdout, ##args);			\
36cfb7
+	} while (0)
36cfb7
+
36cfb7
 #define pr_out_sp(num, args...)					\
36cfb7
 	do {							\
36cfb7
 		int ret = fprintf(stdout, ##args);		\
36cfb7
@@ -42,6 +50,35 @@
36cfb7
 			fprintf(stdout, "%*s", num - ret, "");	\
36cfb7
 	} while (0)
36cfb7
 
36cfb7
+static int g_indent_level;
36cfb7
+static bool g_indent_newline;
36cfb7
+#define INDENT_STR_STEP 2
36cfb7
+#define INDENT_STR_MAXLEN 32
36cfb7
+static char g_indent_str[INDENT_STR_MAXLEN + 1] = "";
36cfb7
+
36cfb7
+static void __pr_out_indent_inc(void)
36cfb7
+{
36cfb7
+	if (g_indent_level + INDENT_STR_STEP > INDENT_STR_MAXLEN)
36cfb7
+		return;
36cfb7
+	g_indent_level += INDENT_STR_STEP;
36cfb7
+	memset(g_indent_str, ' ', sizeof(g_indent_str));
36cfb7
+	g_indent_str[g_indent_level] = '\0';
36cfb7
+}
36cfb7
+
36cfb7
+static void __pr_out_indent_dec(void)
36cfb7
+{
36cfb7
+	if (g_indent_level - INDENT_STR_STEP < 0)
36cfb7
+		return;
36cfb7
+	g_indent_level -= INDENT_STR_STEP;
36cfb7
+	g_indent_str[g_indent_level] = '\0';
36cfb7
+}
36cfb7
+
36cfb7
+static void __pr_out_newline(void)
36cfb7
+{
36cfb7
+	pr_out("\n");
36cfb7
+	g_indent_newline = true;
36cfb7
+}
36cfb7
+
36cfb7
 static int _mnlg_socket_recv_run(struct mnlg_socket *nlg,
36cfb7
 				 mnl_cb_t data_cb, void *data)
36cfb7
 {
36cfb7
@@ -137,6 +174,8 @@ static void ifname_map_free(struct ifname_map *ifname_map)
36cfb7
 #define DL_OPT_SB_TC		BIT(10)
36cfb7
 #define DL_OPT_ESWITCH_MODE	BIT(11)
36cfb7
 #define DL_OPT_ESWITCH_INLINE_MODE	BIT(12)
36cfb7
+#define DL_OPT_DPIPE_TABLE_NAME	BIT(13)
36cfb7
+#define DL_OPT_DPIPE_TABLE_COUNTERS	BIT(14)
36cfb7
 
36cfb7
 struct dl_opts {
36cfb7
 	uint32_t present; /* flags of present items */
36cfb7
@@ -154,6 +193,8 @@ struct dl_opts {
36cfb7
 	uint16_t sb_tc_index;
36cfb7
 	enum devlink_eswitch_mode eswitch_mode;
36cfb7
 	enum devlink_eswitch_inline_mode eswitch_inline_mode;
36cfb7
+	const char *dpipe_table_name;
36cfb7
+	bool dpipe_counters_enable;
36cfb7
 };
36cfb7
 
36cfb7
 struct dl {
36cfb7
@@ -166,6 +207,7 @@ struct dl {
36cfb7
 	json_writer_t *jw;
36cfb7
 	bool json_output;
36cfb7
 	bool pretty_output;
36cfb7
+	bool verbose;
36cfb7
 	struct {
36cfb7
 		bool present;
36cfb7
 		char *bus_name;
36cfb7
@@ -257,6 +299,38 @@ static const enum mnl_attr_data_type devlink_policy[DEVLINK_ATTR_MAX + 1] = {
36cfb7
 	[DEVLINK_ATTR_SB_OCC_MAX] = MNL_TYPE_U32,
36cfb7
 	[DEVLINK_ATTR_ESWITCH_MODE] = MNL_TYPE_U16,
36cfb7
 	[DEVLINK_ATTR_ESWITCH_INLINE_MODE] = MNL_TYPE_U8,
36cfb7
+	[DEVLINK_ATTR_DPIPE_TABLES] = MNL_TYPE_NESTED,
36cfb7
+	[DEVLINK_ATTR_DPIPE_TABLE] = MNL_TYPE_NESTED,
36cfb7
+	[DEVLINK_ATTR_DPIPE_TABLE_NAME] = MNL_TYPE_STRING,
36cfb7
+	[DEVLINK_ATTR_DPIPE_TABLE_SIZE] = MNL_TYPE_U64,
36cfb7
+	[DEVLINK_ATTR_DPIPE_TABLE_MATCHES] = MNL_TYPE_NESTED,
36cfb7
+	[DEVLINK_ATTR_DPIPE_TABLE_ACTIONS] = MNL_TYPE_NESTED,
36cfb7
+	[DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED] =  MNL_TYPE_U8,
36cfb7
+	[DEVLINK_ATTR_DPIPE_ENTRIES] = MNL_TYPE_NESTED,
36cfb7
+	[DEVLINK_ATTR_DPIPE_ENTRY] = MNL_TYPE_NESTED,
36cfb7
+	[DEVLINK_ATTR_DPIPE_ENTRY_INDEX] = MNL_TYPE_U64,
36cfb7
+	[DEVLINK_ATTR_DPIPE_ENTRY_MATCH_VALUES] = MNL_TYPE_NESTED,
36cfb7
+	[DEVLINK_ATTR_DPIPE_ENTRY_ACTION_VALUES] = MNL_TYPE_NESTED,
36cfb7
+	[DEVLINK_ATTR_DPIPE_ENTRY_COUNTER] = MNL_TYPE_U64,
36cfb7
+	[DEVLINK_ATTR_DPIPE_MATCH] = MNL_TYPE_NESTED,
36cfb7
+	[DEVLINK_ATTR_DPIPE_MATCH_VALUE] = MNL_TYPE_NESTED,
36cfb7
+	[DEVLINK_ATTR_DPIPE_MATCH_TYPE] = MNL_TYPE_U32,
36cfb7
+	[DEVLINK_ATTR_DPIPE_ACTION] = MNL_TYPE_NESTED,
36cfb7
+	[DEVLINK_ATTR_DPIPE_ACTION_VALUE] = MNL_TYPE_NESTED,
36cfb7
+	[DEVLINK_ATTR_DPIPE_ACTION_TYPE] = MNL_TYPE_U32,
36cfb7
+	[DEVLINK_ATTR_DPIPE_VALUE_MAPPING] = MNL_TYPE_U32,
36cfb7
+	[DEVLINK_ATTR_DPIPE_HEADERS] = MNL_TYPE_NESTED,
36cfb7
+	[DEVLINK_ATTR_DPIPE_HEADER] = MNL_TYPE_NESTED,
36cfb7
+	[DEVLINK_ATTR_DPIPE_HEADER_NAME] = MNL_TYPE_STRING,
36cfb7
+	[DEVLINK_ATTR_DPIPE_HEADER_ID] = MNL_TYPE_U32,
36cfb7
+	[DEVLINK_ATTR_DPIPE_HEADER_FIELDS] = MNL_TYPE_NESTED,
36cfb7
+	[DEVLINK_ATTR_DPIPE_HEADER_GLOBAL] = MNL_TYPE_U8,
36cfb7
+	[DEVLINK_ATTR_DPIPE_HEADER_INDEX] = MNL_TYPE_U32,
36cfb7
+	[DEVLINK_ATTR_DPIPE_FIELD] = MNL_TYPE_NESTED,
36cfb7
+	[DEVLINK_ATTR_DPIPE_FIELD_NAME] = MNL_TYPE_STRING,
36cfb7
+	[DEVLINK_ATTR_DPIPE_FIELD_ID] = MNL_TYPE_U32,
36cfb7
+	[DEVLINK_ATTR_DPIPE_FIELD_BITWIDTH] = MNL_TYPE_U32,
36cfb7
+	[DEVLINK_ATTR_DPIPE_FIELD_MAPPING_TYPE] = MNL_TYPE_U32,
36cfb7
 };
36cfb7
 
36cfb7
 static int attr_cb(const struct nlattr *attr, void *data)
36cfb7
@@ -666,6 +740,20 @@ static int eswitch_inline_mode_get(const char *typestr,
36cfb7
 	return 0;
36cfb7
 }
36cfb7
 
36cfb7
+static int dpipe_counters_enable_get(const char *typestr,
36cfb7
+				     bool *counters_enable)
36cfb7
+{
36cfb7
+	if (strcmp(typestr, "enable") == 0) {
36cfb7
+		*counters_enable = 1;
36cfb7
+	} else if (strcmp(typestr, "disable") == 0) {
36cfb7
+		*counters_enable = 0;
36cfb7
+	} else {
36cfb7
+		pr_err("Unknown counter_state \"%s\"\n", typestr);
36cfb7
+		return -EINVAL;
36cfb7
+	}
36cfb7
+	return 0;
36cfb7
+}
36cfb7
+
36cfb7
 static int dl_argv_parse(struct dl *dl, uint32_t o_required,
36cfb7
 			 uint32_t o_optional)
36cfb7
 {
36cfb7
@@ -800,6 +888,27 @@ static int dl_argv_parse(struct dl *dl, uint32_t o_required,
36cfb7
 			if (err)
36cfb7
 				return err;
36cfb7
 			o_found |= DL_OPT_ESWITCH_INLINE_MODE;
36cfb7
+		} else if (dl_argv_match(dl, "name") &&
36cfb7
+			   (o_all & DL_OPT_DPIPE_TABLE_NAME)) {
36cfb7
+			dl_arg_inc(dl);
36cfb7
+			err = dl_argv_str(dl, &opts->dpipe_table_name);
36cfb7
+			if (err)
36cfb7
+				return err;
36cfb7
+			o_found |= DL_OPT_DPIPE_TABLE_NAME;
36cfb7
+		} else if (dl_argv_match(dl, "counters") &&
36cfb7
+			   (o_all & DL_OPT_DPIPE_TABLE_COUNTERS)) {
36cfb7
+			const char *typestr;
36cfb7
+
36cfb7
+			dl_arg_inc(dl);
36cfb7
+			err = dl_argv_str(dl, &typestr);
36cfb7
+			if (err)
36cfb7
+				return err;
36cfb7
+			err = dpipe_counters_enable_get(typestr,
36cfb7
+							&opts->dpipe_counters_enable);
36cfb7
+			if (err)
36cfb7
+				return err;
36cfb7
+			o_found |= DL_OPT_DPIPE_TABLE_COUNTERS;
36cfb7
+
36cfb7
 		} else {
36cfb7
 			pr_err("Unknown option \"%s\"\n", dl_argv(dl));
36cfb7
 			return -EINVAL;
36cfb7
@@ -866,6 +975,17 @@ static int dl_argv_parse(struct dl *dl, uint32_t o_required,
36cfb7
 		return -EINVAL;
36cfb7
 	}
36cfb7
 
36cfb7
+	if ((o_required & DL_OPT_DPIPE_TABLE_NAME) &&
36cfb7
+	    !(o_found & DL_OPT_DPIPE_TABLE_NAME)) {
36cfb7
+		pr_err("Dpipe table name expected\n");
36cfb7
+		return -EINVAL;
36cfb7
+	}
36cfb7
+
36cfb7
+	if ((o_required & DL_OPT_DPIPE_TABLE_COUNTERS) &&
36cfb7
+	    !(o_found & DL_OPT_DPIPE_TABLE_COUNTERS)) {
36cfb7
+		pr_err("Dpipe table counter state expected\n");
36cfb7
+		return -EINVAL;
36cfb7
+	}
36cfb7
 	return 0;
36cfb7
 }
36cfb7
 
36cfb7
@@ -915,6 +1035,12 @@ static void dl_opts_put(struct nlmsghdr *nlh, struct dl *dl)
36cfb7
 	if (opts->present & DL_OPT_ESWITCH_INLINE_MODE)
36cfb7
 		mnl_attr_put_u8(nlh, DEVLINK_ATTR_ESWITCH_INLINE_MODE,
36cfb7
 				opts->eswitch_inline_mode);
36cfb7
+	if (opts->present & DL_OPT_DPIPE_TABLE_NAME)
36cfb7
+		mnl_attr_put_strz(nlh, DEVLINK_ATTR_DPIPE_TABLE_NAME,
36cfb7
+				  opts->dpipe_table_name);
36cfb7
+	if (opts->present & DL_OPT_DPIPE_TABLE_COUNTERS)
36cfb7
+		mnl_attr_put_u8(nlh, DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED,
36cfb7
+				opts->dpipe_counters_enable);
36cfb7
 }
36cfb7
 
36cfb7
 static int dl_argv_parse_put(struct nlmsghdr *nlh, struct dl *dl,
36cfb7
@@ -1033,7 +1159,19 @@ static void __pr_out_handle_start(struct dl *dl, struct nlattr **tb,
36cfb7
 			jsonw_start_object(dl->jw);
36cfb7
 		}
36cfb7
 	} else {
36cfb7
-		pr_out("%s%s", buf, content ? ":" : "");
36cfb7
+		if (array) {
36cfb7
+			if (should_arr_last_handle_end(dl, bus_name, dev_name))
36cfb7
+				__pr_out_indent_dec();
36cfb7
+			if (should_arr_last_handle_start(dl, bus_name,
36cfb7
+							 dev_name)) {
36cfb7
+				pr_out("%s%s", buf, content ? ":" : "");
36cfb7
+				__pr_out_newline();
36cfb7
+				__pr_out_indent_inc();
36cfb7
+				arr_last_handle_set(dl, bus_name, dev_name);
36cfb7
+			}
36cfb7
+		} else {
36cfb7
+			pr_out("%s%s", buf, content ? ":" : "");
36cfb7
+		}
36cfb7
 	}
36cfb7
 }
36cfb7
 
36cfb7
@@ -1047,7 +1185,7 @@ static void pr_out_handle_end(struct dl *dl)
36cfb7
 	if (dl->json_output)
36cfb7
 		jsonw_end_object(dl->jw);
36cfb7
 	else
36cfb7
-		pr_out("\n");
36cfb7
+		__pr_out_newline();
36cfb7
 }
36cfb7
 
36cfb7
 static void pr_out_handle(struct dl *dl, struct nlattr **tb)
36cfb7
@@ -1163,18 +1301,26 @@ static void pr_out_port_handle_end(struct dl *dl)
36cfb7
 
36cfb7
 static void pr_out_str(struct dl *dl, const char *name, const char *val)
36cfb7
 {
36cfb7
-	if (dl->json_output)
36cfb7
+	if (dl->json_output) {
36cfb7
 		jsonw_string_field(dl->jw, name, val);
36cfb7
-	else
36cfb7
-		pr_out(" %s %s", name, val);
36cfb7
+	} else {
36cfb7
+		if (g_indent_newline)
36cfb7
+			pr_out("%s %s", name, val);
36cfb7
+		else
36cfb7
+			pr_out(" %s %s", name, val);
36cfb7
+	}
36cfb7
 }
36cfb7
 
36cfb7
 static void pr_out_uint(struct dl *dl, const char *name, unsigned int val)
36cfb7
 {
36cfb7
-	if (dl->json_output)
36cfb7
+	if (dl->json_output) {
36cfb7
 		jsonw_uint_field(dl->jw, name, val);
36cfb7
-	else
36cfb7
-		pr_out(" %s %u", name, val);
36cfb7
+	} else {
36cfb7
+		if (g_indent_newline)
36cfb7
+			pr_out("%s %u", name, val);
36cfb7
+		else
36cfb7
+			pr_out(" %s %u", name, val);
36cfb7
+	}
36cfb7
 }
36cfb7
 
36cfb7
 static void pr_out_dev(struct dl *dl, struct nlattr **tb)
36cfb7
@@ -1201,6 +1347,42 @@ static void pr_out_section_end(struct dl *dl)
36cfb7
 	}
36cfb7
 }
36cfb7
 
36cfb7
+static void pr_out_array_start(struct dl *dl, const char *name)
36cfb7
+{
36cfb7
+	if (dl->json_output) {
36cfb7
+		jsonw_name(dl->jw, name);
36cfb7
+		jsonw_start_array(dl->jw);
36cfb7
+	} else {
36cfb7
+		if (!g_indent_newline)
36cfb7
+			__pr_out_newline();
36cfb7
+		pr_out("%s:", name);
36cfb7
+		__pr_out_newline();
36cfb7
+		__pr_out_indent_inc();
36cfb7
+	}
36cfb7
+}
36cfb7
+
36cfb7
+static void pr_out_array_end(struct dl *dl)
36cfb7
+{
36cfb7
+	if (dl->json_output)
36cfb7
+		jsonw_end_array(dl->jw);
36cfb7
+	else
36cfb7
+		__pr_out_indent_dec();
36cfb7
+}
36cfb7
+
36cfb7
+static void pr_out_entry_start(struct dl *dl)
36cfb7
+{
36cfb7
+	if (dl->json_output)
36cfb7
+		jsonw_start_object(dl->jw);
36cfb7
+}
36cfb7
+
36cfb7
+static void pr_out_entry_end(struct dl *dl)
36cfb7
+{
36cfb7
+	if (dl->json_output)
36cfb7
+		jsonw_end_object(dl->jw);
36cfb7
+	else
36cfb7
+		__pr_out_newline();
36cfb7
+}
36cfb7
+
36cfb7
 static const char *eswitch_mode_name(uint32_t mode)
36cfb7
 {
36cfb7
 	switch (mode) {
36cfb7
@@ -2423,129 +2605,1102 @@ static int cmd_mon(struct dl *dl)
36cfb7
 	return -ENOENT;
36cfb7
 }
36cfb7
 
36cfb7
-static void help(void)
36cfb7
+struct dpipe_field {
36cfb7
+	char *name;
36cfb7
+	unsigned int id;
36cfb7
+	unsigned int bitwidth;
36cfb7
+	enum devlink_dpipe_field_mapping_type mapping_type;
36cfb7
+};
36cfb7
+
36cfb7
+struct dpipe_header {
36cfb7
+	struct list_head list;
36cfb7
+	char *name;
36cfb7
+	unsigned int id;
36cfb7
+	struct dpipe_field *fields;
36cfb7
+	unsigned int fields_count;
36cfb7
+};
36cfb7
+
36cfb7
+struct dpipe_ctx {
36cfb7
+	struct dl *dl;
36cfb7
+	int err;
36cfb7
+	struct list_head global_headers;
36cfb7
+	struct list_head local_headers;
36cfb7
+	bool print_headers;
36cfb7
+};
36cfb7
+
36cfb7
+static struct dpipe_header *dpipe_header_alloc(unsigned int fields_count)
36cfb7
 {
36cfb7
-	pr_err("Usage: devlink [ OPTIONS ] OBJECT { COMMAND | help }\n"
36cfb7
-	       "where  OBJECT := { dev | port | sb | monitor }\n"
36cfb7
-	       "       OPTIONS := { -V[ersion] | -n[no-nice-names] | -j[json] | -p[pretty] }\n");
36cfb7
+	struct dpipe_header *header;
36cfb7
+
36cfb7
+	header = calloc(1, sizeof(struct dpipe_header));
36cfb7
+	if (!header)
36cfb7
+		return NULL;
36cfb7
+	header->fields = calloc(fields_count, sizeof(struct dpipe_field));
36cfb7
+	if (!header->fields)
36cfb7
+		goto err_fields_alloc;
36cfb7
+	header->fields_count = fields_count;
36cfb7
+	return header;
36cfb7
+
36cfb7
+err_fields_alloc:
36cfb7
+	free(header);
36cfb7
+	return NULL;
36cfb7
 }
36cfb7
 
36cfb7
-static int dl_cmd(struct dl *dl)
36cfb7
+static void dpipe_header_free(struct dpipe_header *header)
36cfb7
 {
36cfb7
-	if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
36cfb7
-		help();
36cfb7
-		return 0;
36cfb7
-	} else if (dl_argv_match(dl, "dev")) {
36cfb7
-		dl_arg_inc(dl);
36cfb7
-		return cmd_dev(dl);
36cfb7
-	} else if (dl_argv_match(dl, "port")) {
36cfb7
-		dl_arg_inc(dl);
36cfb7
-		return cmd_port(dl);
36cfb7
-	} else if (dl_argv_match(dl, "sb")) {
36cfb7
-		dl_arg_inc(dl);
36cfb7
-		return cmd_sb(dl);
36cfb7
-	} else if (dl_argv_match(dl, "monitor")) {
36cfb7
-		dl_arg_inc(dl);
36cfb7
-		return cmd_mon(dl);
36cfb7
+	free(header->fields);
36cfb7
+	free(header);
36cfb7
+}
36cfb7
+
36cfb7
+static void dpipe_header_clear(struct dpipe_header *header)
36cfb7
+{
36cfb7
+	struct dpipe_field *field;
36cfb7
+	int i;
36cfb7
+
36cfb7
+	for (i = 0; i < header->fields_count; i++) {
36cfb7
+		field = &header->fields[i];
36cfb7
+		free(field->name);
36cfb7
 	}
36cfb7
-	pr_err("Object \"%s\" not found\n", dl_argv(dl));
36cfb7
-	return -ENOENT;
36cfb7
+	free(header->name);
36cfb7
 }
36cfb7
 
36cfb7
-static int dl_init(struct dl *dl, int argc, char **argv)
36cfb7
+static void dpipe_header_add(struct dpipe_ctx *ctx,
36cfb7
+			     struct dpipe_header *header, bool global)
36cfb7
 {
36cfb7
-	int err;
36cfb7
+	if (global)
36cfb7
+		list_add(&header->list, &ctx->global_headers);
36cfb7
+	else
36cfb7
+		list_add(&header->list, &ctx->local_headers);
36cfb7
+}
36cfb7
 
36cfb7
-	dl->argc = argc;
36cfb7
-	dl->argv = argv;
36cfb7
+static void dpipe_header_del(struct dpipe_header *header)
36cfb7
+{
36cfb7
+	list_del(&header->list);
36cfb7
+}
36cfb7
 
36cfb7
-	dl->nlg = mnlg_socket_open(DEVLINK_GENL_NAME, DEVLINK_GENL_VERSION);
36cfb7
-	if (!dl->nlg) {
36cfb7
-		pr_err("Failed to connect to devlink Netlink\n");
36cfb7
-		return -errno;
36cfb7
+static struct dpipe_ctx *dpipe_ctx_alloc(struct dl *dl)
36cfb7
+{
36cfb7
+	struct dpipe_ctx *ctx;
36cfb7
+
36cfb7
+	ctx = calloc(1, sizeof(struct dpipe_ctx));
36cfb7
+	if (!ctx)
36cfb7
+		return NULL;
36cfb7
+	ctx->dl = dl;
36cfb7
+	INIT_LIST_HEAD(&ctx->global_headers);
36cfb7
+	INIT_LIST_HEAD(&ctx->local_headers);
36cfb7
+	return ctx;
36cfb7
+}
36cfb7
+
36cfb7
+static void dpipe_ctx_free(struct dpipe_ctx *ctx)
36cfb7
+{
36cfb7
+	free(ctx);
36cfb7
+}
36cfb7
+
36cfb7
+static void dpipe_ctx_clear(struct dpipe_ctx *ctx)
36cfb7
+{
36cfb7
+	struct dpipe_header *header, *tmp;
36cfb7
+
36cfb7
+	list_for_each_entry_safe(header, tmp, &ctx->global_headers,
36cfb7
+				 list) {
36cfb7
+		dpipe_header_del(header);
36cfb7
+		dpipe_header_clear(header);
36cfb7
+		dpipe_header_free(header);
36cfb7
+	}
36cfb7
+	list_for_each_entry_safe(header, tmp, &ctx->local_headers,
36cfb7
+				 list) {
36cfb7
+		dpipe_header_del(header);
36cfb7
+		dpipe_header_clear(header);
36cfb7
+		dpipe_header_free(header);
36cfb7
 	}
36cfb7
+}
36cfb7
 
36cfb7
-	err = ifname_map_init(dl);
36cfb7
-	if (err) {
36cfb7
-		pr_err("Failed to create index map\n");
36cfb7
-		goto err_ifname_map_create;
36cfb7
+static const char *dpipe_header_id2s(struct dpipe_ctx *ctx,
36cfb7
+				     uint32_t header_id, bool global)
36cfb7
+{
36cfb7
+	struct list_head *header_list;
36cfb7
+	struct dpipe_header *header;
36cfb7
+
36cfb7
+	if (global)
36cfb7
+		header_list = &ctx->global_headers;
36cfb7
+	else
36cfb7
+		header_list = &ctx->local_headers;
36cfb7
+	list_for_each_entry(header, header_list, list) {
36cfb7
+		if (header->id != header_id)
36cfb7
+			continue;
36cfb7
+		return header->name;
36cfb7
 	}
36cfb7
-	if (dl->json_output) {
36cfb7
-		dl->jw = jsonw_new(stdout);
36cfb7
-		if (!dl->jw) {
36cfb7
-			pr_err("Failed to create JSON writer\n");
36cfb7
-			goto err_json_new;
36cfb7
-		}
36cfb7
-		jsonw_pretty(dl->jw, dl->pretty_output);
36cfb7
+	return NULL;
36cfb7
+}
36cfb7
+
36cfb7
+static const char *dpipe_field_id2s(struct dpipe_ctx *ctx,
36cfb7
+				    uint32_t header_id,
36cfb7
+				    uint32_t field_id, bool global)
36cfb7
+{
36cfb7
+	struct list_head *header_list;
36cfb7
+	struct dpipe_header *header;
36cfb7
+
36cfb7
+	if (global)
36cfb7
+		header_list = &ctx->global_headers;
36cfb7
+	else
36cfb7
+		header_list = &ctx->local_headers;
36cfb7
+	list_for_each_entry(header, header_list, list) {
36cfb7
+		if (header->id != header_id)
36cfb7
+			continue;
36cfb7
+		return header->fields[field_id].name;
36cfb7
 	}
36cfb7
-	return 0;
36cfb7
+	return NULL;
36cfb7
+}
36cfb7
 
36cfb7
-err_json_new:
36cfb7
-	ifname_map_fini(dl);
36cfb7
-err_ifname_map_create:
36cfb7
-	mnlg_socket_close(dl->nlg);
36cfb7
-	return err;
36cfb7
+static const char *
36cfb7
+dpipe_field_mapping_e2s(enum devlink_dpipe_field_mapping_type mapping_type)
36cfb7
+{
36cfb7
+	switch (mapping_type) {
36cfb7
+	case DEVLINK_DPIPE_FIELD_MAPPING_TYPE_NONE:
36cfb7
+		return NULL;
36cfb7
+	case DEVLINK_DPIPE_FIELD_MAPPING_TYPE_IFINDEX:
36cfb7
+		return "ifindex";
36cfb7
+	default:
36cfb7
+		return "<unknown>";
36cfb7
+	}
36cfb7
 }
36cfb7
 
36cfb7
-static void dl_fini(struct dl *dl)
36cfb7
+static const char *
36cfb7
+dpipe_mapping_get(struct dpipe_ctx *ctx, uint32_t header_id,
36cfb7
+		  uint32_t field_id, bool global)
36cfb7
 {
36cfb7
-	if (dl->json_output)
36cfb7
-		jsonw_destroy(&dl->jw);
36cfb7
-	ifname_map_fini(dl);
36cfb7
-	mnlg_socket_close(dl->nlg);
36cfb7
+	enum devlink_dpipe_field_mapping_type mapping_type;
36cfb7
+	struct list_head *header_list;
36cfb7
+	struct dpipe_header *header;
36cfb7
+
36cfb7
+	if (global)
36cfb7
+		header_list = &ctx->global_headers;
36cfb7
+	else
36cfb7
+		header_list = &ctx->local_headers;
36cfb7
+	list_for_each_entry(header, header_list, list) {
36cfb7
+		if (header->id != header_id)
36cfb7
+			continue;
36cfb7
+		mapping_type = header->fields[field_id].mapping_type;
36cfb7
+		return dpipe_field_mapping_e2s(mapping_type);
36cfb7
+	}
36cfb7
+	return NULL;
36cfb7
 }
36cfb7
 
36cfb7
-static struct dl *dl_alloc(void)
36cfb7
+static void pr_out_dpipe_fields(struct dpipe_ctx *ctx,
36cfb7
+				struct dpipe_field *fields,
36cfb7
+				unsigned int field_count)
36cfb7
 {
36cfb7
-	struct dl *dl;
36cfb7
+	struct dpipe_field *field;
36cfb7
+	int i;
36cfb7
 
36cfb7
-	dl = calloc(1, sizeof(*dl));
36cfb7
-	if (!dl)
36cfb7
-		return NULL;
36cfb7
-	return dl;
36cfb7
+	for (i = 0; i < field_count; i++) {
36cfb7
+		field = &fields[i];
36cfb7
+		pr_out_entry_start(ctx->dl);
36cfb7
+		pr_out_str(ctx->dl, "name", field->name);
36cfb7
+		if (ctx->dl->verbose)
36cfb7
+			pr_out_uint(ctx->dl, "id", field->id);
36cfb7
+		pr_out_uint(ctx->dl, "bitwidth", field->bitwidth);
36cfb7
+		if (field->mapping_type)
36cfb7
+			pr_out_str(ctx->dl, "mapping_type",
36cfb7
+				   dpipe_field_mapping_e2s(field->mapping_type));
36cfb7
+		pr_out_entry_end(ctx->dl);
36cfb7
+	}
36cfb7
 }
36cfb7
 
36cfb7
-static void dl_free(struct dl *dl)
36cfb7
+static void
36cfb7
+pr_out_dpipe_header(struct dpipe_ctx *ctx, struct nlattr **tb,
36cfb7
+		    struct dpipe_header *header, bool global)
36cfb7
 {
36cfb7
-	free(dl);
36cfb7
+	pr_out_handle_start_arr(ctx->dl, tb);
36cfb7
+	pr_out_str(ctx->dl, "name", header->name);
36cfb7
+	if (ctx->dl->verbose) {
36cfb7
+		pr_out_uint(ctx->dl, "id", header->id);
36cfb7
+		pr_out_str(ctx->dl, "global",
36cfb7
+			   global ? "true" : "false");
36cfb7
+	}
36cfb7
+	pr_out_array_start(ctx->dl, "field");
36cfb7
+	pr_out_dpipe_fields(ctx, header->fields,
36cfb7
+			    header->fields_count);
36cfb7
+	pr_out_array_end(ctx->dl);
36cfb7
+	pr_out_handle_end(ctx->dl);
36cfb7
 }
36cfb7
 
36cfb7
-int main(int argc, char **argv)
36cfb7
+static void pr_out_dpipe_headers(struct dpipe_ctx *ctx,
36cfb7
+				 struct nlattr **tb)
36cfb7
 {
36cfb7
-	static const struct option long_options[] = {
36cfb7
-		{ "Version",		no_argument,		NULL, 'V' },
36cfb7
-		{ "no-nice-names",	no_argument,		NULL, 'n' },
36cfb7
-		{ "json",		no_argument,		NULL, 'j' },
36cfb7
-		{ "pretty",		no_argument,		NULL, 'p' },
36cfb7
-		{ NULL, 0, NULL, 0 }
36cfb7
-	};
36cfb7
-	struct dl *dl;
36cfb7
-	int opt;
36cfb7
+	struct dpipe_header *header;
36cfb7
+
36cfb7
+	list_for_each_entry(header, &ctx->local_headers, list)
36cfb7
+		pr_out_dpipe_header(ctx, tb, header, false);
36cfb7
+
36cfb7
+	list_for_each_entry(header, &ctx->global_headers, list)
36cfb7
+		pr_out_dpipe_header(ctx, tb, header, true);
36cfb7
+}
36cfb7
+
36cfb7
+static int dpipe_header_field_get(struct nlattr *nl, struct dpipe_field *field)
36cfb7
+{
36cfb7
+	struct nlattr *nla_field[DEVLINK_ATTR_MAX + 1] = {};
36cfb7
+	const char *name;
36cfb7
 	int err;
36cfb7
-	int ret;
36cfb7
 
36cfb7
-	dl = dl_alloc();
36cfb7
-	if (!dl) {
36cfb7
-		pr_err("Failed to allocate memory for devlink\n");
36cfb7
-		return EXIT_FAILURE;
36cfb7
+	err = mnl_attr_parse_nested(nl, attr_cb, nla_field);
36cfb7
+	if (err != MNL_CB_OK)
36cfb7
+		return -EINVAL;
36cfb7
+	if (!nla_field[DEVLINK_ATTR_DPIPE_FIELD_ID] ||
36cfb7
+	    !nla_field[DEVLINK_ATTR_DPIPE_FIELD_NAME] ||
36cfb7
+	    !nla_field[DEVLINK_ATTR_DPIPE_FIELD_BITWIDTH] ||
36cfb7
+	    !nla_field[DEVLINK_ATTR_DPIPE_FIELD_MAPPING_TYPE])
36cfb7
+		return -EINVAL;
36cfb7
+
36cfb7
+	name = mnl_attr_get_str(nla_field[DEVLINK_ATTR_DPIPE_FIELD_NAME]);
36cfb7
+	field->id = mnl_attr_get_u32(nla_field[DEVLINK_ATTR_DPIPE_FIELD_ID]);
36cfb7
+	field->bitwidth = mnl_attr_get_u32(nla_field[DEVLINK_ATTR_DPIPE_FIELD_BITWIDTH]);
36cfb7
+	field->name = strdup(name);
36cfb7
+	if (!field->name)
36cfb7
+		return -ENOMEM;
36cfb7
+	field->mapping_type = mnl_attr_get_u32(nla_field[DEVLINK_ATTR_DPIPE_FIELD_MAPPING_TYPE]);
36cfb7
+	return 0;
36cfb7
+}
36cfb7
+
36cfb7
+static int dpipe_header_fields_get(struct nlattr *nla_fields,
36cfb7
+				   struct dpipe_field *fields)
36cfb7
+{
36cfb7
+	struct nlattr *nla_field;
36cfb7
+	int count = 0;
36cfb7
+	int err;
36cfb7
+
36cfb7
+	mnl_attr_for_each_nested(nla_field, nla_fields) {
36cfb7
+		err = dpipe_header_field_get(nla_field, &fields[count]);
36cfb7
+		if (err)
36cfb7
+			return err;
36cfb7
+		count++;
36cfb7
 	}
36cfb7
+	return 0;
36cfb7
+}
36cfb7
 
36cfb7
-	while ((opt = getopt_long(argc, argv, "Vnjp",
36cfb7
-				  long_options, NULL)) >= 0) {
36cfb7
+static unsigned int dpipe_header_field_count_get(struct nlattr *nla_fields)
36cfb7
+{
36cfb7
+	struct nlattr *nla_field;
36cfb7
+	unsigned int count = 0;
36cfb7
 
36cfb7
-		switch (opt) {
36cfb7
-		case 'V':
36cfb7
-			printf("devlink utility, iproute2-ss%s\n", SNAPSHOT);
36cfb7
-			ret = EXIT_SUCCESS;
36cfb7
-			goto dl_free;
36cfb7
-		case 'n':
36cfb7
-			dl->no_nice_names = true;
36cfb7
-			break;
36cfb7
-		case 'j':
36cfb7
-			dl->json_output = true;
36cfb7
-			break;
36cfb7
-		case 'p':
36cfb7
-			dl->pretty_output = true;
36cfb7
+	mnl_attr_for_each_nested(nla_field, nla_fields)
36cfb7
+		count++;
36cfb7
+	return count;
36cfb7
+}
36cfb7
+
36cfb7
+static int dpipe_header_get(struct dpipe_ctx *ctx, struct nlattr *nl)
36cfb7
+{
36cfb7
+	struct nlattr *nla_header[DEVLINK_ATTR_MAX + 1] = {};
36cfb7
+	struct dpipe_header *header;
36cfb7
+	unsigned int fields_count;
36cfb7
+	const char *header_name;
36cfb7
+	bool global;
36cfb7
+	int err;
36cfb7
+
36cfb7
+	err = mnl_attr_parse_nested(nl, attr_cb, nla_header);
36cfb7
+	if (err != MNL_CB_OK)
36cfb7
+		return -EINVAL;
36cfb7
+
36cfb7
+	if (!nla_header[DEVLINK_ATTR_DPIPE_HEADER_NAME] ||
36cfb7
+	    !nla_header[DEVLINK_ATTR_DPIPE_HEADER_ID] ||
36cfb7
+	    !nla_header[DEVLINK_ATTR_DPIPE_HEADER_FIELDS])
36cfb7
+		return -EINVAL;
36cfb7
+
36cfb7
+	fields_count = dpipe_header_field_count_get(nla_header[DEVLINK_ATTR_DPIPE_HEADER_FIELDS]);
36cfb7
+	header = dpipe_header_alloc(fields_count);
36cfb7
+	if (!header)
36cfb7
+		return -ENOMEM;
36cfb7
+
36cfb7
+	header_name = mnl_attr_get_str(nla_header[DEVLINK_ATTR_DPIPE_HEADER_NAME]);
36cfb7
+	header->name = strdup(header_name);
36cfb7
+	header->id = mnl_attr_get_u32(nla_header[DEVLINK_ATTR_DPIPE_HEADER_ID]);
36cfb7
+	header->fields_count = fields_count;
36cfb7
+	global = !!mnl_attr_get_u8(nla_header[DEVLINK_ATTR_DPIPE_HEADER_GLOBAL]);
36cfb7
+
36cfb7
+	err = dpipe_header_fields_get(nla_header[DEVLINK_ATTR_DPIPE_HEADER_FIELDS],
36cfb7
+				      header->fields);
36cfb7
+	if (err)
36cfb7
+		goto err_field_get;
36cfb7
+	dpipe_header_add(ctx, header, global);
36cfb7
+	return 0;
36cfb7
+
36cfb7
+err_field_get:
36cfb7
+	dpipe_header_free(header);
36cfb7
+	return err;
36cfb7
+}
36cfb7
+
36cfb7
+static int dpipe_headers_get(struct dpipe_ctx *ctx, struct nlattr **tb)
36cfb7
+{
36cfb7
+	struct nlattr *nla_headers = tb[DEVLINK_ATTR_DPIPE_HEADERS];
36cfb7
+	struct nlattr *nla_header;
36cfb7
+	int err;
36cfb7
+
36cfb7
+	mnl_attr_for_each_nested(nla_header, nla_headers) {
36cfb7
+		err = dpipe_header_get(ctx, nla_header);
36cfb7
+		if (err)
36cfb7
+			return err;
36cfb7
+	}
36cfb7
+	return 0;
36cfb7
+}
36cfb7
+
36cfb7
+static int cmd_dpipe_header_cb(const struct nlmsghdr *nlh, void *data)
36cfb7
+{
36cfb7
+	struct dpipe_ctx *ctx = data;
36cfb7
+	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
36cfb7
+	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
36cfb7
+	int err;
36cfb7
+
36cfb7
+	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
36cfb7
+	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
36cfb7
+	    !tb[DEVLINK_ATTR_DPIPE_HEADERS])
36cfb7
+		return MNL_CB_ERROR;
36cfb7
+	err = dpipe_headers_get(ctx, tb);
36cfb7
+	if (err) {
36cfb7
+		ctx->err = err;
36cfb7
+		return MNL_CB_ERROR;
36cfb7
+	}
36cfb7
+
36cfb7
+	if (ctx->print_headers)
36cfb7
+		pr_out_dpipe_headers(ctx, tb);
36cfb7
+	return MNL_CB_OK;
36cfb7
+}
36cfb7
+
36cfb7
+static int cmd_dpipe_headers_show(struct dl *dl)
36cfb7
+{
36cfb7
+	struct nlmsghdr *nlh;
36cfb7
+	struct dpipe_ctx *ctx;
36cfb7
+	uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
36cfb7
+	int err;
36cfb7
+
36cfb7
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_HEADERS_GET, flags);
36cfb7
+
36cfb7
+	err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, 0);
36cfb7
+	if (err)
36cfb7
+		return err;
36cfb7
+
36cfb7
+	ctx = dpipe_ctx_alloc(dl);
36cfb7
+	if (!ctx)
36cfb7
+		return -ENOMEM;
36cfb7
+
36cfb7
+	ctx->print_headers = true;
36cfb7
+
36cfb7
+	pr_out_section_start(dl, "header");
36cfb7
+	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_header_cb, ctx);
36cfb7
+	if (err)
36cfb7
+		pr_err("error get headers %s\n", strerror(ctx->err));
36cfb7
+	pr_out_section_end(dl);
36cfb7
+
36cfb7
+	dpipe_ctx_clear(ctx);
36cfb7
+	dpipe_ctx_free(ctx);
36cfb7
+	return err;
36cfb7
+}
36cfb7
+
36cfb7
+static void cmd_dpipe_header_help(void)
36cfb7
+{
36cfb7
+	pr_err("Usage: devlink dpipe headers show DEV\n");
36cfb7
+}
36cfb7
+
36cfb7
+static int cmd_dpipe_header(struct dl *dl)
36cfb7
+{
36cfb7
+	if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
36cfb7
+		cmd_dpipe_header_help();
36cfb7
+		return 0;
36cfb7
+	} else if (dl_argv_match(dl, "show")) {
36cfb7
+		dl_arg_inc(dl);
36cfb7
+		return cmd_dpipe_headers_show(dl);
36cfb7
+	}
36cfb7
+	pr_err("Command \"%s\" not found\n", dl_argv(dl));
36cfb7
+	return -ENOENT;
36cfb7
+}
36cfb7
+
36cfb7
+static const char
36cfb7
+*dpipe_action_type_e2s(enum devlink_dpipe_action_type action_type)
36cfb7
+{
36cfb7
+	switch (action_type) {
36cfb7
+	case DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY:
36cfb7
+		return "field_modify";
36cfb7
+	default:
36cfb7
+		return "<unknown>";
36cfb7
+	}
36cfb7
+}
36cfb7
+
36cfb7
+static void pr_out_dpipe_action(struct dpipe_ctx *ctx,
36cfb7
+				uint32_t header_id, uint32_t field_id,
36cfb7
+				uint32_t action_type, bool global)
36cfb7
+{
36cfb7
+	const char *mapping;
36cfb7
+
36cfb7
+	pr_out_str(ctx->dl, "type", dpipe_action_type_e2s(action_type));
36cfb7
+	pr_out_str(ctx->dl, "header", dpipe_header_id2s(ctx, header_id,
36cfb7
+							global));
36cfb7
+	pr_out_str(ctx->dl, "field", dpipe_field_id2s(ctx, header_id, field_id,
36cfb7
+						      global));
36cfb7
+	mapping = dpipe_mapping_get(ctx, header_id, field_id, global);
36cfb7
+	if (mapping)
36cfb7
+		pr_out_str(ctx->dl, "mapping", mapping);
36cfb7
+}
36cfb7
+
36cfb7
+static int dpipe_action_show(struct dpipe_ctx *ctx, struct nlattr *nl)
36cfb7
+{
36cfb7
+	struct nlattr *nla_action[DEVLINK_ATTR_MAX + 1] = {};
36cfb7
+	uint32_t header_id, field_id, action_type;
36cfb7
+	bool global;
36cfb7
+	int err;
36cfb7
+
36cfb7
+	err = mnl_attr_parse_nested(nl, attr_cb, nla_action);
36cfb7
+	if (err != MNL_CB_OK)
36cfb7
+		return -EINVAL;
36cfb7
+
36cfb7
+	if (!nla_action[DEVLINK_ATTR_DPIPE_ACTION_TYPE] ||
36cfb7
+	    !nla_action[DEVLINK_ATTR_DPIPE_HEADER_INDEX] ||
36cfb7
+	    !nla_action[DEVLINK_ATTR_DPIPE_HEADER_ID] ||
36cfb7
+	    !nla_action[DEVLINK_ATTR_DPIPE_FIELD_ID]) {
36cfb7
+		return -EINVAL;
36cfb7
+	}
36cfb7
+
36cfb7
+	header_id = mnl_attr_get_u32(nla_action[DEVLINK_ATTR_DPIPE_HEADER_ID]);
36cfb7
+	field_id = mnl_attr_get_u32(nla_action[DEVLINK_ATTR_DPIPE_FIELD_ID]);
36cfb7
+	action_type = mnl_attr_get_u32(nla_action[DEVLINK_ATTR_DPIPE_ACTION_TYPE]);
36cfb7
+	global = !!mnl_attr_get_u8(nla_action[DEVLINK_ATTR_DPIPE_HEADER_GLOBAL]);
36cfb7
+
36cfb7
+	pr_out_dpipe_action(ctx, header_id, field_id, action_type, global);
36cfb7
+	return 0;
36cfb7
+}
36cfb7
+
36cfb7
+static int dpipe_table_actions_show(struct dpipe_ctx *ctx,
36cfb7
+				    struct nlattr *nla_actions)
36cfb7
+{
36cfb7
+	struct nlattr *nla_action;
36cfb7
+
36cfb7
+	mnl_attr_for_each_nested(nla_action, nla_actions) {
36cfb7
+		pr_out_entry_start(ctx->dl);
36cfb7
+		if (dpipe_action_show(ctx, nla_action))
36cfb7
+			goto err_action_show;
36cfb7
+		pr_out_entry_end(ctx->dl);
36cfb7
+	}
36cfb7
+	return 0;
36cfb7
+
36cfb7
+err_action_show:
36cfb7
+	pr_out_entry_end(ctx->dl);
36cfb7
+	return -EINVAL;
36cfb7
+}
36cfb7
+
36cfb7
+static const char *
36cfb7
+dpipe_match_type_e2s(enum devlink_dpipe_match_type match_type)
36cfb7
+{
36cfb7
+	switch (match_type) {
36cfb7
+	case DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT:
36cfb7
+		return "field_exact";
36cfb7
+	default:
36cfb7
+		return "<unknown>";
36cfb7
+	}
36cfb7
+}
36cfb7
+
36cfb7
+static void pr_out_dpipe_match(struct dpipe_ctx *ctx,
36cfb7
+			       uint32_t header_id, uint32_t field_id,
36cfb7
+			       uint32_t match_type, bool global)
36cfb7
+{
36cfb7
+	const char *mapping;
36cfb7
+
36cfb7
+	pr_out_str(ctx->dl, "type", dpipe_match_type_e2s(match_type));
36cfb7
+	pr_out_str(ctx->dl, "header", dpipe_header_id2s(ctx, header_id,
36cfb7
+							global));
36cfb7
+	pr_out_str(ctx->dl, "field", dpipe_field_id2s(ctx, header_id, field_id,
36cfb7
+						      global));
36cfb7
+	mapping = dpipe_mapping_get(ctx, header_id, field_id, global);
36cfb7
+	if (mapping)
36cfb7
+		pr_out_str(ctx->dl, "mapping", mapping);
36cfb7
+
36cfb7
+}
36cfb7
+
36cfb7
+static int dpipe_match_show(struct dpipe_ctx *ctx, struct nlattr *nl)
36cfb7
+{
36cfb7
+	struct nlattr *nla_match[DEVLINK_ATTR_MAX + 1] = {};
36cfb7
+	uint32_t header_id, field_id, match_type;
36cfb7
+	bool global;
36cfb7
+	int err;
36cfb7
+
36cfb7
+	err = mnl_attr_parse_nested(nl, attr_cb, nla_match);
36cfb7
+	if (err != MNL_CB_OK)
36cfb7
+		return -EINVAL;
36cfb7
+
36cfb7
+	if (!nla_match[DEVLINK_ATTR_DPIPE_MATCH_TYPE] ||
36cfb7
+	    !nla_match[DEVLINK_ATTR_DPIPE_HEADER_INDEX] ||
36cfb7
+	    !nla_match[DEVLINK_ATTR_DPIPE_HEADER_ID] ||
36cfb7
+	    !nla_match[DEVLINK_ATTR_DPIPE_FIELD_ID]) {
36cfb7
+		return -EINVAL;
36cfb7
+	}
36cfb7
+
36cfb7
+	match_type = mnl_attr_get_u32(nla_match[DEVLINK_ATTR_DPIPE_MATCH_TYPE]);
36cfb7
+	header_id = mnl_attr_get_u32(nla_match[DEVLINK_ATTR_DPIPE_HEADER_ID]);
36cfb7
+	field_id = mnl_attr_get_u32(nla_match[DEVLINK_ATTR_DPIPE_FIELD_ID]);
36cfb7
+	global = !!mnl_attr_get_u8(nla_match[DEVLINK_ATTR_DPIPE_HEADER_GLOBAL]);
36cfb7
+
36cfb7
+	pr_out_dpipe_match(ctx, header_id, field_id, match_type, global);
36cfb7
+	return 0;
36cfb7
+}
36cfb7
+
36cfb7
+static int dpipe_table_matches_show(struct dpipe_ctx *ctx,
36cfb7
+				    struct nlattr *nla_matches)
36cfb7
+{
36cfb7
+	struct nlattr *nla_match;
36cfb7
+
36cfb7
+	mnl_attr_for_each_nested(nla_match, nla_matches) {
36cfb7
+		pr_out_entry_start(ctx->dl);
36cfb7
+		if (dpipe_match_show(ctx, nla_match))
36cfb7
+			goto err_match_show;
36cfb7
+		pr_out_entry_end(ctx->dl);
36cfb7
+	}
36cfb7
+	return 0;
36cfb7
+
36cfb7
+err_match_show:
36cfb7
+	pr_out_entry_end(ctx->dl);
36cfb7
+	return -EINVAL;
36cfb7
+}
36cfb7
+
36cfb7
+static int dpipe_table_show(struct dpipe_ctx *ctx, struct nlattr *nl)
36cfb7
+{
36cfb7
+	struct nlattr *nla_table[DEVLINK_ATTR_MAX + 1] = {};
36cfb7
+	bool counters_enabled;
36cfb7
+	const char *name;
36cfb7
+	uint32_t size;
36cfb7
+	int err;
36cfb7
+
36cfb7
+	err = mnl_attr_parse_nested(nl, attr_cb, nla_table);
36cfb7
+	if (err != MNL_CB_OK)
36cfb7
+		return -EINVAL;
36cfb7
+
36cfb7
+	if (!nla_table[DEVLINK_ATTR_DPIPE_TABLE_NAME] ||
36cfb7
+	    !nla_table[DEVLINK_ATTR_DPIPE_TABLE_SIZE] ||
36cfb7
+	    !nla_table[DEVLINK_ATTR_DPIPE_TABLE_ACTIONS] ||
36cfb7
+	    !nla_table[DEVLINK_ATTR_DPIPE_TABLE_MATCHES] ||
36cfb7
+	    !nla_table[DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED]) {
36cfb7
+		return -EINVAL;
36cfb7
+	}
36cfb7
+
36cfb7
+	name = mnl_attr_get_str(nla_table[DEVLINK_ATTR_DPIPE_TABLE_NAME]);
36cfb7
+	size = mnl_attr_get_u32(nla_table[DEVLINK_ATTR_DPIPE_TABLE_SIZE]);
36cfb7
+	counters_enabled = !!mnl_attr_get_u8(nla_table[DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED]);
36cfb7
+
36cfb7
+	pr_out_str(ctx->dl, "name", name);
36cfb7
+	pr_out_uint(ctx->dl, "size", size);
36cfb7
+	pr_out_str(ctx->dl, "counters_enabled",
36cfb7
+		   counters_enabled ? "true" : "false");
36cfb7
+
36cfb7
+	pr_out_array_start(ctx->dl, "match");
36cfb7
+	if (dpipe_table_matches_show(ctx, nla_table[DEVLINK_ATTR_DPIPE_TABLE_MATCHES]))
36cfb7
+		goto err_matches_show;
36cfb7
+	pr_out_array_end(ctx->dl);
36cfb7
+
36cfb7
+	pr_out_array_start(ctx->dl, "action");
36cfb7
+	if (dpipe_table_actions_show(ctx, nla_table[DEVLINK_ATTR_DPIPE_TABLE_ACTIONS]))
36cfb7
+		goto err_actions_show;
36cfb7
+	pr_out_array_end(ctx->dl);
36cfb7
+
36cfb7
+	return 0;
36cfb7
+
36cfb7
+err_actions_show:
36cfb7
+err_matches_show:
36cfb7
+	pr_out_array_end(ctx->dl);
36cfb7
+	return -EINVAL;
36cfb7
+}
36cfb7
+
36cfb7
+static int dpipe_tables_show(struct dpipe_ctx *ctx, struct nlattr **tb)
36cfb7
+{
36cfb7
+	struct nlattr *nla_tables = tb[DEVLINK_ATTR_DPIPE_TABLES];
36cfb7
+	struct nlattr *nla_table;
36cfb7
+
36cfb7
+	mnl_attr_for_each_nested(nla_table, nla_tables) {
36cfb7
+		pr_out_handle_start_arr(ctx->dl, tb);
36cfb7
+		if (dpipe_table_show(ctx, nla_table))
36cfb7
+			goto err_table_show;
36cfb7
+		pr_out_handle_end(ctx->dl);
36cfb7
+	}
36cfb7
+	return 0;
36cfb7
+
36cfb7
+err_table_show:
36cfb7
+	pr_out_handle_end(ctx->dl);
36cfb7
+	return -EINVAL;
36cfb7
+}
36cfb7
+
36cfb7
+static int cmd_dpipe_table_show_cb(const struct nlmsghdr *nlh, void *data)
36cfb7
+{
36cfb7
+	struct dpipe_ctx *ctx = data;
36cfb7
+	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
36cfb7
+	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
36cfb7
+
36cfb7
+	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
36cfb7
+	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
36cfb7
+	    !tb[DEVLINK_ATTR_DPIPE_TABLES])
36cfb7
+		return MNL_CB_ERROR;
36cfb7
+
36cfb7
+	if (dpipe_tables_show(ctx, tb))
36cfb7
+		return MNL_CB_ERROR;
36cfb7
+	return MNL_CB_OK;
36cfb7
+}
36cfb7
+
36cfb7
+static int cmd_dpipe_table_show(struct dl *dl)
36cfb7
+{
36cfb7
+	struct nlmsghdr *nlh;
36cfb7
+	struct dpipe_ctx *ctx;
36cfb7
+	uint16_t flags = NLM_F_REQUEST;
36cfb7
+	int err;
36cfb7
+
36cfb7
+	ctx = dpipe_ctx_alloc(dl);
36cfb7
+	if (!ctx)
36cfb7
+		return -ENOMEM;
36cfb7
+
36cfb7
+	err = dl_argv_parse(dl, DL_OPT_HANDLE, DL_OPT_DPIPE_TABLE_NAME);
36cfb7
+	if (err)
36cfb7
+		goto out;
36cfb7
+
36cfb7
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_HEADERS_GET, flags);
36cfb7
+	dl_opts_put(nlh, dl);
36cfb7
+	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_header_cb, ctx);
36cfb7
+	if (err) {
36cfb7
+		pr_err("error get headers %s\n", strerror(ctx->err));
36cfb7
+		goto out;
36cfb7
+	}
36cfb7
+
36cfb7
+	flags = NLM_F_REQUEST | NLM_F_ACK;
36cfb7
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_TABLE_GET, flags);
36cfb7
+	dl_opts_put(nlh, dl);
36cfb7
+
36cfb7
+	pr_out_section_start(dl, "table");
36cfb7
+	_mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_table_show_cb, ctx);
36cfb7
+	pr_out_section_end(dl);
36cfb7
+out:
36cfb7
+	dpipe_ctx_clear(ctx);
36cfb7
+	dpipe_ctx_free(ctx);
36cfb7
+	return err;
36cfb7
+}
36cfb7
+
36cfb7
+static int cmd_dpipe_table_set(struct dl *dl)
36cfb7
+{
36cfb7
+	struct nlmsghdr *nlh;
36cfb7
+	int err;
36cfb7
+
36cfb7
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_TABLE_COUNTERS_SET,
36cfb7
+			       NLM_F_REQUEST | NLM_F_ACK);
36cfb7
+
36cfb7
+	err = dl_argv_parse_put(nlh, dl,
36cfb7
+				DL_OPT_HANDLE | DL_OPT_DPIPE_TABLE_NAME |
36cfb7
+				DL_OPT_DPIPE_TABLE_COUNTERS, 0);
36cfb7
+	if (err)
36cfb7
+		return err;
36cfb7
+
36cfb7
+	return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
36cfb7
+}
36cfb7
+
36cfb7
+static int dpipe_entry_value_show(struct dpipe_ctx *ctx,
36cfb7
+				  struct nlattr **nla_match_value)
36cfb7
+{
36cfb7
+	uint16_t value_len;
36cfb7
+	bool mask, mapping;
36cfb7
+
36cfb7
+	mask = !!nla_match_value[DEVLINK_ATTR_DPIPE_VALUE_MASK];
36cfb7
+	mapping = !!nla_match_value[DEVLINK_ATTR_DPIPE_VALUE_MAPPING];
36cfb7
+
36cfb7
+	value_len = mnl_attr_get_payload_len(nla_match_value[DEVLINK_ATTR_DPIPE_VALUE]);
36cfb7
+	if (value_len == sizeof(uint32_t)) {
36cfb7
+		uint32_t value, value_mask, value_mapping;
36cfb7
+
36cfb7
+		if (mapping) {
36cfb7
+			value_mapping = mnl_attr_get_u32(nla_match_value[DEVLINK_ATTR_DPIPE_VALUE_MAPPING]);
36cfb7
+			pr_out_uint(ctx->dl, "mapping_value", value_mapping);
36cfb7
+		}
36cfb7
+
36cfb7
+		if (mask) {
36cfb7
+			value_mask = mnl_attr_get_u32(nla_match_value[DEVLINK_ATTR_DPIPE_VALUE_MASK]);
36cfb7
+			pr_out_uint(ctx->dl, "mask_value", value_mask);
36cfb7
+		}
36cfb7
+
36cfb7
+		value = mnl_attr_get_u32(nla_match_value[DEVLINK_ATTR_DPIPE_VALUE]);
36cfb7
+		pr_out_uint(ctx->dl, "value", value);
36cfb7
+
36cfb7
+	} else {
36cfb7
+		return -EINVAL;
36cfb7
+	}
36cfb7
+
36cfb7
+	return 0;
36cfb7
+}
36cfb7
+
36cfb7
+static int dpipe_entry_match_value_show(struct dpipe_ctx *ctx,
36cfb7
+					struct nlattr *nl)
36cfb7
+{
36cfb7
+	struct nlattr *nla_match_value[DEVLINK_ATTR_MAX + 1] = {};
36cfb7
+	int err;
36cfb7
+
36cfb7
+	err = mnl_attr_parse_nested(nl, attr_cb, nla_match_value);
36cfb7
+	if (err != MNL_CB_OK)
36cfb7
+		return -EINVAL;
36cfb7
+
36cfb7
+	if (!nla_match_value[DEVLINK_ATTR_DPIPE_MATCH] ||
36cfb7
+	    !nla_match_value[DEVLINK_ATTR_DPIPE_VALUE]) {
36cfb7
+		return -EINVAL;
36cfb7
+	}
36cfb7
+
36cfb7
+	pr_out_entry_start(ctx->dl);
36cfb7
+	if (dpipe_match_show(ctx, nla_match_value[DEVLINK_ATTR_DPIPE_MATCH]))
36cfb7
+		goto err_match_show;
36cfb7
+	if (dpipe_entry_value_show(ctx, nla_match_value))
36cfb7
+		goto err_value_show;
36cfb7
+	pr_out_entry_end(ctx->dl);
36cfb7
+
36cfb7
+	return 0;
36cfb7
+
36cfb7
+err_match_show:
36cfb7
+err_value_show:
36cfb7
+	pr_out_entry_end(ctx->dl);
36cfb7
+	return -EINVAL;
36cfb7
+}
36cfb7
+
36cfb7
+static int dpipe_entry_action_value_show(struct dpipe_ctx *ctx,
36cfb7
+					 struct nlattr *nl)
36cfb7
+{
36cfb7
+	struct nlattr *nla_action_value[DEVLINK_ATTR_MAX + 1] = {};
36cfb7
+	int err;
36cfb7
+
36cfb7
+	err = mnl_attr_parse_nested(nl, attr_cb, nla_action_value);
36cfb7
+	if (err != MNL_CB_OK)
36cfb7
+		return -EINVAL;
36cfb7
+
36cfb7
+	if (!nla_action_value[DEVLINK_ATTR_DPIPE_ACTION] ||
36cfb7
+	    !nla_action_value[DEVLINK_ATTR_DPIPE_VALUE]) {
36cfb7
+		return -EINVAL;
36cfb7
+	}
36cfb7
+
36cfb7
+	pr_out_entry_start(ctx->dl);
36cfb7
+	if (dpipe_action_show(ctx, nla_action_value[DEVLINK_ATTR_DPIPE_ACTION]))
36cfb7
+		goto err_action_show;
36cfb7
+	if (dpipe_entry_value_show(ctx, nla_action_value))
36cfb7
+		goto err_value_show;
36cfb7
+	pr_out_entry_end(ctx->dl);
36cfb7
+
36cfb7
+	return 0;
36cfb7
+
36cfb7
+err_action_show:
36cfb7
+err_value_show:
36cfb7
+	pr_out_entry_end(ctx->dl);
36cfb7
+	return -EINVAL;
36cfb7
+}
36cfb7
+
36cfb7
+static int
36cfb7
+dpipe_tables_action_values_show(struct dpipe_ctx *ctx,
36cfb7
+				struct nlattr *nla_action_values)
36cfb7
+{
36cfb7
+	struct nlattr *nla_action_value;
36cfb7
+
36cfb7
+	mnl_attr_for_each_nested(nla_action_value, nla_action_values) {
36cfb7
+		if (dpipe_entry_action_value_show(ctx, nla_action_value))
36cfb7
+			return -EINVAL;
36cfb7
+	}
36cfb7
+	return 0;
36cfb7
+}
36cfb7
+
36cfb7
+static int
36cfb7
+dpipe_tables_match_values_show(struct dpipe_ctx *ctx,
36cfb7
+			       struct nlattr *nla_match_values)
36cfb7
+{
36cfb7
+	struct nlattr *nla_match_value;
36cfb7
+
36cfb7
+	mnl_attr_for_each_nested(nla_match_value, nla_match_values) {
36cfb7
+		if (dpipe_entry_match_value_show(ctx, nla_match_value))
36cfb7
+			return -EINVAL;
36cfb7
+	}
36cfb7
+	return 0;
36cfb7
+}
36cfb7
+
36cfb7
+static int dpipe_entry_show(struct dpipe_ctx *ctx, struct nlattr *nl)
36cfb7
+{
36cfb7
+	struct nlattr *nla_entry[DEVLINK_ATTR_MAX + 1] = {};
36cfb7
+	uint32_t entry_index;
36cfb7
+	uint64_t counter;
36cfb7
+	int err;
36cfb7
+
36cfb7
+	err = mnl_attr_parse_nested(nl, attr_cb, nla_entry);
36cfb7
+	if (err != MNL_CB_OK)
36cfb7
+		return -EINVAL;
36cfb7
+
36cfb7
+	if (!nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_INDEX] ||
36cfb7
+	    !nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_MATCH_VALUES] ||
36cfb7
+	    !nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_ACTION_VALUES]) {
36cfb7
+		return -EINVAL;
36cfb7
+	}
36cfb7
+
36cfb7
+	entry_index = mnl_attr_get_u32(nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_INDEX]);
36cfb7
+	pr_out_uint(ctx->dl, "index", entry_index);
36cfb7
+
36cfb7
+	if (nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_COUNTER]) {
36cfb7
+		counter = mnl_attr_get_u64(nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_COUNTER]);
36cfb7
+		pr_out_uint(ctx->dl, "counter", counter);
36cfb7
+	}
36cfb7
+
36cfb7
+	pr_out_array_start(ctx->dl, "match_value");
36cfb7
+	if (dpipe_tables_match_values_show(ctx,
36cfb7
+					   nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_MATCH_VALUES]))
36cfb7
+		goto err_match_values_show;
36cfb7
+	pr_out_array_end(ctx->dl);
36cfb7
+
36cfb7
+	pr_out_array_start(ctx->dl, "action_value");
36cfb7
+	if (dpipe_tables_action_values_show(ctx,
36cfb7
+					    nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_ACTION_VALUES]))
36cfb7
+		goto err_action_values_show;
36cfb7
+	pr_out_array_end(ctx->dl);
36cfb7
+	return 0;
36cfb7
+
36cfb7
+err_action_values_show:
36cfb7
+err_match_values_show:
36cfb7
+	pr_out_array_end(ctx->dl);
36cfb7
+	return -EINVAL;
36cfb7
+}
36cfb7
+
36cfb7
+static int dpipe_table_entries_show(struct dpipe_ctx *ctx, struct nlattr **tb)
36cfb7
+{
36cfb7
+	struct nlattr *nla_entries = tb[DEVLINK_ATTR_DPIPE_ENTRIES];
36cfb7
+	struct nlattr *nla_entry;
36cfb7
+
36cfb7
+	mnl_attr_for_each_nested(nla_entry, nla_entries) {
36cfb7
+		pr_out_handle_start_arr(ctx->dl, tb);
36cfb7
+		if (dpipe_entry_show(ctx, nla_entry))
36cfb7
+			goto err_entry_show;
36cfb7
+		pr_out_handle_end(ctx->dl);
36cfb7
+	}
36cfb7
+	return 0;
36cfb7
+
36cfb7
+err_entry_show:
36cfb7
+	pr_out_handle_end(ctx->dl);
36cfb7
+	return -EINVAL;
36cfb7
+}
36cfb7
+
36cfb7
+static int cmd_dpipe_table_entry_dump_cb(const struct nlmsghdr *nlh, void *data)
36cfb7
+{
36cfb7
+	struct dpipe_ctx *ctx = data;
36cfb7
+	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
36cfb7
+	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
36cfb7
+
36cfb7
+	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
36cfb7
+	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
36cfb7
+	    !tb[DEVLINK_ATTR_DPIPE_ENTRIES])
36cfb7
+		return MNL_CB_ERROR;
36cfb7
+
36cfb7
+	if (dpipe_table_entries_show(ctx, tb))
36cfb7
+		return MNL_CB_ERROR;
36cfb7
+	return MNL_CB_OK;
36cfb7
+}
36cfb7
+
36cfb7
+static int cmd_dpipe_table_dump(struct dl *dl)
36cfb7
+{
36cfb7
+	struct nlmsghdr *nlh;
36cfb7
+	struct dpipe_ctx *ctx;
36cfb7
+	uint16_t flags = NLM_F_REQUEST;
36cfb7
+	int err;
36cfb7
+
36cfb7
+	ctx = dpipe_ctx_alloc(dl);
36cfb7
+	if (!ctx)
36cfb7
+		return -ENOMEM;
36cfb7
+
36cfb7
+	err = dl_argv_parse(dl, DL_OPT_HANDLE | DL_OPT_DPIPE_TABLE_NAME, 0);
36cfb7
+	if (err)
36cfb7
+		goto out;
36cfb7
+
36cfb7
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_HEADERS_GET, flags);
36cfb7
+	dl_opts_put(nlh, dl);
36cfb7
+	err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_header_cb, ctx);
36cfb7
+	if (err) {
36cfb7
+		pr_err("error get headers %s\n", strerror(ctx->err));
36cfb7
+		goto out;
36cfb7
+	}
36cfb7
+
36cfb7
+	flags = NLM_F_REQUEST | NLM_F_ACK;
36cfb7
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_ENTRIES_GET, flags);
36cfb7
+	dl_opts_put(nlh, dl);
36cfb7
+
36cfb7
+	pr_out_section_start(dl, "table_entry");
36cfb7
+	_mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_table_entry_dump_cb, ctx);
36cfb7
+	pr_out_section_end(dl);
36cfb7
+out:
36cfb7
+	dpipe_ctx_clear(ctx);
36cfb7
+	dpipe_ctx_free(ctx);
36cfb7
+	return err;
36cfb7
+}
36cfb7
+
36cfb7
+static void cmd_dpipe_table_help(void)
36cfb7
+{
36cfb7
+	pr_err("Usage: devlink dpipe table [ OBJECT-LIST ]\n"
36cfb7
+	       "where  OBJECT-LIST := { show | set | dump }\n");
36cfb7
+}
36cfb7
+
36cfb7
+static int cmd_dpipe_table(struct dl *dl)
36cfb7
+{
36cfb7
+	if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
36cfb7
+		cmd_dpipe_table_help();
36cfb7
+		return 0;
36cfb7
+	} else if (dl_argv_match(dl, "show")) {
36cfb7
+		dl_arg_inc(dl);
36cfb7
+		return cmd_dpipe_table_show(dl);
36cfb7
+	} else if (dl_argv_match(dl, "set")) {
36cfb7
+		dl_arg_inc(dl);
36cfb7
+		return cmd_dpipe_table_set(dl);
36cfb7
+	}  else if (dl_argv_match(dl, "dump")) {
36cfb7
+		dl_arg_inc(dl);
36cfb7
+		return cmd_dpipe_table_dump(dl);
36cfb7
+	}
36cfb7
+	pr_err("Command \"%s\" not found\n", dl_argv(dl));
36cfb7
+	return -ENOENT;
36cfb7
+}
36cfb7
+
36cfb7
+static void cmd_dpipe_help(void)
36cfb7
+{
36cfb7
+	pr_err("Usage: devlink dpipe [ OBJECT-LIST ]\n"
36cfb7
+	       "where  OBJECT-LIST := { header | table }\n");
36cfb7
+}
36cfb7
+
36cfb7
+static int cmd_dpipe(struct dl *dl)
36cfb7
+{
36cfb7
+	if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
36cfb7
+		cmd_dpipe_help();
36cfb7
+		return 0;
36cfb7
+	} else if (dl_argv_match(dl, "header")) {
36cfb7
+		dl_arg_inc(dl);
36cfb7
+		return cmd_dpipe_header(dl);
36cfb7
+	} else if (dl_argv_match(dl, "table")) {
36cfb7
+		dl_arg_inc(dl);
36cfb7
+		return cmd_dpipe_table(dl);
36cfb7
+	}
36cfb7
+	pr_err("Command \"%s\" not found\n", dl_argv(dl));
36cfb7
+	return -ENOENT;
36cfb7
+}
36cfb7
+
36cfb7
+static void help(void)
36cfb7
+{
36cfb7
+	pr_err("Usage: devlink [ OPTIONS ] OBJECT { COMMAND | help }\n"
36cfb7
+	       "where  OBJECT := { dev | port | sb | monitor | dpipe }\n"
36cfb7
+	       "       OPTIONS := { -V[ersion] | -n[no-nice-names] | -j[json] | -p[pretty] | -v[verbose] }\n");
36cfb7
+}
36cfb7
+
36cfb7
+static int dl_cmd(struct dl *dl)
36cfb7
+{
36cfb7
+	if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
36cfb7
+		help();
36cfb7
+		return 0;
36cfb7
+	} else if (dl_argv_match(dl, "dev")) {
36cfb7
+		dl_arg_inc(dl);
36cfb7
+		return cmd_dev(dl);
36cfb7
+	} else if (dl_argv_match(dl, "port")) {
36cfb7
+		dl_arg_inc(dl);
36cfb7
+		return cmd_port(dl);
36cfb7
+	} else if (dl_argv_match(dl, "sb")) {
36cfb7
+		dl_arg_inc(dl);
36cfb7
+		return cmd_sb(dl);
36cfb7
+	} else if (dl_argv_match(dl, "monitor")) {
36cfb7
+		dl_arg_inc(dl);
36cfb7
+		return cmd_mon(dl);
36cfb7
+	} else if (dl_argv_match(dl, "dpipe")) {
36cfb7
+		dl_arg_inc(dl);
36cfb7
+		return cmd_dpipe(dl);
36cfb7
+	}
36cfb7
+	pr_err("Object \"%s\" not found\n", dl_argv(dl));
36cfb7
+	return -ENOENT;
36cfb7
+}
36cfb7
+
36cfb7
+static int dl_init(struct dl *dl, int argc, char **argv)
36cfb7
+{
36cfb7
+	int err;
36cfb7
+
36cfb7
+	dl->argc = argc;
36cfb7
+	dl->argv = argv;
36cfb7
+
36cfb7
+	dl->nlg = mnlg_socket_open(DEVLINK_GENL_NAME, DEVLINK_GENL_VERSION);
36cfb7
+	if (!dl->nlg) {
36cfb7
+		pr_err("Failed to connect to devlink Netlink\n");
36cfb7
+		return -errno;
36cfb7
+	}
36cfb7
+
36cfb7
+	err = ifname_map_init(dl);
36cfb7
+	if (err) {
36cfb7
+		pr_err("Failed to create index map\n");
36cfb7
+		goto err_ifname_map_create;
36cfb7
+	}
36cfb7
+	if (dl->json_output) {
36cfb7
+		dl->jw = jsonw_new(stdout);
36cfb7
+		if (!dl->jw) {
36cfb7
+			pr_err("Failed to create JSON writer\n");
36cfb7
+			goto err_json_new;
36cfb7
+		}
36cfb7
+		jsonw_pretty(dl->jw, dl->pretty_output);
36cfb7
+	}
36cfb7
+	return 0;
36cfb7
+
36cfb7
+err_json_new:
36cfb7
+	ifname_map_fini(dl);
36cfb7
+err_ifname_map_create:
36cfb7
+	mnlg_socket_close(dl->nlg);
36cfb7
+	return err;
36cfb7
+}
36cfb7
+
36cfb7
+static void dl_fini(struct dl *dl)
36cfb7
+{
36cfb7
+	if (dl->json_output)
36cfb7
+		jsonw_destroy(&dl->jw);
36cfb7
+	ifname_map_fini(dl);
36cfb7
+	mnlg_socket_close(dl->nlg);
36cfb7
+}
36cfb7
+
36cfb7
+static struct dl *dl_alloc(void)
36cfb7
+{
36cfb7
+	struct dl *dl;
36cfb7
+
36cfb7
+	dl = calloc(1, sizeof(*dl));
36cfb7
+	if (!dl)
36cfb7
+		return NULL;
36cfb7
+	return dl;
36cfb7
+}
36cfb7
+
36cfb7
+static void dl_free(struct dl *dl)
36cfb7
+{
36cfb7
+	free(dl);
36cfb7
+}
36cfb7
+
36cfb7
+int main(int argc, char **argv)
36cfb7
+{
36cfb7
+	static const struct option long_options[] = {
36cfb7
+		{ "Version",		no_argument,		NULL, 'V' },
36cfb7
+		{ "no-nice-names",	no_argument,		NULL, 'n' },
36cfb7
+		{ "json",		no_argument,		NULL, 'j' },
36cfb7
+		{ "pretty",		no_argument,		NULL, 'p' },
36cfb7
+		{ "verbose",		no_argument,		NULL, 'v' },
36cfb7
+		{ NULL, 0, NULL, 0 }
36cfb7
+	};
36cfb7
+	struct dl *dl;
36cfb7
+	int opt;
36cfb7
+	int err;
36cfb7
+	int ret;
36cfb7
+
36cfb7
+	dl = dl_alloc();
36cfb7
+	if (!dl) {
36cfb7
+		pr_err("Failed to allocate memory for devlink\n");
36cfb7
+		return EXIT_FAILURE;
36cfb7
+	}
36cfb7
+
36cfb7
+	while ((opt = getopt_long(argc, argv, "Vnjpv",
36cfb7
+				  long_options, NULL)) >= 0) {
36cfb7
+
36cfb7
+		switch (opt) {
36cfb7
+		case 'V':
36cfb7
+			printf("devlink utility, iproute2-ss%s\n", SNAPSHOT);
36cfb7
+			ret = EXIT_SUCCESS;
36cfb7
+			goto dl_free;
36cfb7
+		case 'n':
36cfb7
+			dl->no_nice_names = true;
36cfb7
+			break;
36cfb7
+		case 'j':
36cfb7
+			dl->json_output = true;
36cfb7
+			break;
36cfb7
+		case 'p':
36cfb7
+			dl->pretty_output = true;
36cfb7
+			break;
36cfb7
+		case 'v':
36cfb7
+			dl->verbose = true;
36cfb7
 			break;
36cfb7
 		default:
36cfb7
 			pr_err("Unknown option.\n");
36cfb7
-- 
36cfb7
1.8.3.1
36cfb7