Netdev List
 help / color / mirror / Atom feed
* [PATCH iproute2-next 03/11] ip: Add a new family of commands, "stats"
From: Petr Machata @ 2022-04-22  8:30 UTC (permalink / raw)
  To: netdev; +Cc: David Ahern, Stephen Hemminger, Ido Schimmel, Petr Machata
In-Reply-To: <cover.1650615982.git.petrm@nvidia.com>

Add a core of a new frontend tool for interfacing with the RTM_*STATS
family of messages. The following patches will add subcommands for showing
and setting individual statistics suites.

Note that in this patch, "ip stats" is made to be an invalid command line.
This will be changed in later patches to default to "show" when that is
introduced.

Signed-off-by: Petr Machata <petrm@nvidia.com>
Reviewed-by: Ido Schimmel <idosch@nvidia.com>
---
 ip/Makefile    |  3 ++-
 ip/ip.c        |  1 +
 ip/ip_common.h |  1 +
 ip/ipstats.c   | 31 +++++++++++++++++++++++++++++++
 4 files changed, 35 insertions(+), 1 deletion(-)
 create mode 100644 ip/ipstats.c

diff --git a/ip/Makefile b/ip/Makefile
index e06a7c84..6c2e0720 100644
--- a/ip/Makefile
+++ b/ip/Makefile
@@ -12,7 +12,8 @@ IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o ipnetns.o \
     iplink_geneve.o iplink_vrf.o iproute_lwtunnel.o ipmacsec.o ipila.o \
     ipvrf.o iplink_xstats.o ipseg6.o iplink_netdevsim.o iplink_rmnet.o \
     ipnexthop.o ipmptcp.o iplink_bareudp.o iplink_wwan.o ipioam6.o \
-    iplink_amt.o iplink_batadv.o iplink_gtp.o iplink_virt_wifi.o
+    iplink_amt.o iplink_batadv.o iplink_gtp.o iplink_virt_wifi.o \
+    ipstats.o
 
 RTMONOBJ=rtmon.o
 
diff --git a/ip/ip.c b/ip/ip.c
index c784f819..82282bab 100644
--- a/ip/ip.c
+++ b/ip/ip.c
@@ -123,6 +123,7 @@ static const struct cmd {
 	{ "mptcp",	do_mptcp },
 	{ "ioam",	do_ioam6 },
 	{ "help",	do_help },
+	{ "stats",	do_ipstats },
 	{ 0 }
 };
 
diff --git a/ip/ip_common.h b/ip/ip_common.h
index 51a7edc7..53866d7a 100644
--- a/ip/ip_common.h
+++ b/ip/ip_common.h
@@ -90,6 +90,7 @@ int do_seg6(int argc, char **argv);
 int do_ipnh(int argc, char **argv);
 int do_mptcp(int argc, char **argv);
 int do_ioam6(int argc, char **argv);
+int do_ipstats(int argc, char **argv);
 
 int iplink_get(char *name, __u32 filt_mask);
 int iplink_ifla_xstats(int argc, char **argv);
diff --git a/ip/ipstats.c b/ip/ipstats.c
new file mode 100644
index 00000000..099e18a2
--- /dev/null
+++ b/ip/ipstats.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0+
+#include "utils.h"
+#include "ip_common.h"
+
+static int do_help(void)
+{
+	fprintf(stderr,
+		"Usage: ip stats help\n"
+		);
+
+	return 0;
+}
+
+int do_ipstats(int argc, char **argv)
+{
+	int rc;
+
+	if (argc == 0) {
+		do_help();
+		rc = -1;
+	} else if (strcmp(*argv, "help") == 0) {
+		do_help();
+		rc = 0;
+	} else {
+		fprintf(stderr, "Command \"%s\" is unknown, try \"ip stats help\".\n",
+			*argv);
+		rc = -1;
+	}
+
+	return rc;
+}
-- 
2.31.1


^ permalink raw reply related

* [PATCH iproute2-next 04/11] ipstats: Add a "set" command
From: Petr Machata @ 2022-04-22  8:30 UTC (permalink / raw)
  To: netdev; +Cc: David Ahern, Stephen Hemminger, Ido Schimmel, Petr Machata
In-Reply-To: <cover.1650615982.git.petrm@nvidia.com>

Add a command to allow toggling HW stats. An example usage:

 # ip stats set dev swp1 l3_stats on

Signed-off-by: Petr Machata <petrm@nvidia.com>
Reviewed-by: Ido Schimmel <idosch@nvidia.com>
---
 ip/ipstats.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/ip/ipstats.c b/ip/ipstats.c
index 099e18a2..1f5b3f77 100644
--- a/ip/ipstats.c
+++ b/ip/ipstats.c
@@ -1,4 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
+#include <errno.h>
+
 #include "utils.h"
 #include "ip_common.h"
 
@@ -6,11 +8,85 @@ static int do_help(void)
 {
 	fprintf(stderr,
 		"Usage: ip stats help\n"
+		"       ip stats set dev DEV l3_stats { on | off }\n"
 		);
 
 	return 0;
 }
 
+static int ipstats_set_do(int ifindex, int at, bool enable)
+{
+	struct ipstats_req req = {
+		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct if_stats_msg)),
+		.nlh.nlmsg_flags = NLM_F_REQUEST,
+		.nlh.nlmsg_type = RTM_SETSTATS,
+		.ifsm.family = PF_UNSPEC,
+		.ifsm.ifindex = ifindex,
+	};
+
+	addattr8(&req.nlh, sizeof(req), at, enable);
+
+	if (rtnl_talk(&rth, &req.nlh, NULL) < 0)
+		return -2;
+	return 0;
+}
+
+static int ipstats_set(int argc, char **argv)
+{
+	const char *dev = NULL;
+	bool enable = false;
+	int ifindex;
+	int at = 0;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "dev") == 0) {
+			NEXT_ARG();
+			if (dev)
+				duparg2("dev", *argv);
+			if (check_ifname(*argv))
+				invarg("\"dev\" not a valid ifname", *argv);
+			dev = *argv;
+		} else if (strcmp(*argv, "l3_stats") == 0) {
+			int err;
+
+			NEXT_ARG();
+			if (at) {
+				fprintf(stderr, "A statistics suite to toggle was already given.\n");
+				return -EINVAL;
+			}
+			at = IFLA_STATS_SET_OFFLOAD_XSTATS_L3_STATS;
+			enable = parse_on_off("l3_stats", *argv, &err);
+			if (err)
+				return err;
+		} else if (strcmp(*argv, "help") == 0) {
+			do_help();
+			return 0;
+		} else {
+			fprintf(stderr, "What is \"%s\"?\n", *argv);
+			do_help();
+			return -EINVAL;
+		}
+
+		NEXT_ARG_FWD();
+	}
+
+	if (!dev) {
+		fprintf(stderr, "Not enough information: \"dev\" argument is required.\n");
+		exit(-1);
+	}
+
+	if (!at) {
+		fprintf(stderr, "Not enough information: stat type to toggle is required.\n");
+		exit(-1);
+	}
+
+	ifindex = ll_name_to_index(dev);
+	if (!ifindex)
+		return nodev(dev);
+
+	return ipstats_set_do(ifindex, at, enable);
+}
+
 int do_ipstats(int argc, char **argv)
 {
 	int rc;
@@ -21,6 +97,8 @@ int do_ipstats(int argc, char **argv)
 	} else if (strcmp(*argv, "help") == 0) {
 		do_help();
 		rc = 0;
+	} else if (strcmp(*argv, "set") == 0) {
+		rc = ipstats_set(argc-1, argv+1);
 	} else {
 		fprintf(stderr, "Command \"%s\" is unknown, try \"ip stats help\".\n",
 			*argv);
-- 
2.31.1


^ permalink raw reply related

* [PATCH iproute2-next 07/11] ipstats: Add a group "offload", subgroup "cpu_hit"
From: Petr Machata @ 2022-04-22  8:30 UTC (permalink / raw)
  To: netdev; +Cc: David Ahern, Stephen Hemminger, Ido Schimmel, Petr Machata
In-Reply-To: <cover.1650615982.git.petrm@nvidia.com>

Add a new group, "offload", for showing counters from the
IFLA_STATS_LINK_OFFLOAD_XSTATS nest, and a subgroup "cpu_hit" for the
IFLA_OFFLOAD_XSTATS_CPU_HIT stats suite.

For example:

 # ip stats show dev swp1 group offload subgroup cpu_hit
 4178: swp1: group offload subgroup cpu_hit
     RX:  bytes packets errors dropped  missed   mcast
          45522     353      0       0       0       0
     TX:  bytes packets errors dropped carrier collsns
          46054     355      0       0       0       0

 # ip -j stats show dev swp1 group offload subgroup cpu_hit | jq
 [
   {
     "ifindex": 4178,
     "ifname": "swp1",
     "group": "offload",
     "subgroup": "cpu_hit",
     "stats64": {
       "rx": {
         "bytes": 45522,
         "packets": 353,
         "errors": 0,
         "dropped": 0,
         "over_errors": 0,
         "multicast": 0
       },
       "tx": {
         "bytes": 46054,
         "packets": 355,
         "errors": 0,
         "dropped": 0,
         "carrier_errors": 0,
         "collisions": 0
       }
     }
   }
 ]

Signed-off-by: Petr Machata <petrm@nvidia.com>
Reviewed-by: Ido Schimmel <idosch@nvidia.com>
---
 ip/ipstats.c | 37 +++++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/ip/ipstats.c b/ip/ipstats.c
index e4f97ddd..283fba4e 100644
--- a/ip/ipstats.c
+++ b/ip/ipstats.c
@@ -164,6 +164,42 @@ static int ipstats_show_64(struct ipstats_stat_show_attrs *attrs,
 	return 0;
 }
 
+static void
+ipstats_stat_desc_pack_cpu_hit(struct ipstats_stat_dump_filters *filters,
+			       const struct ipstats_stat_desc *desc)
+{
+	ipstats_stat_desc_enable_bit(filters,
+				     IFLA_STATS_LINK_OFFLOAD_XSTATS,
+				     IFLA_OFFLOAD_XSTATS_CPU_HIT);
+}
+
+static int ipstats_stat_desc_show_cpu_hit(struct ipstats_stat_show_attrs *attrs,
+					  const struct ipstats_stat_desc *desc)
+{
+	print_nl();
+	return ipstats_show_64(attrs,
+			       IFLA_STATS_LINK_OFFLOAD_XSTATS,
+			       IFLA_OFFLOAD_XSTATS_CPU_HIT);
+}
+
+static const struct ipstats_stat_desc ipstats_stat_desc_offload_cpu_hit = {
+	.name = "cpu_hit",
+	.kind = IPSTATS_STAT_DESC_KIND_LEAF,
+	.pack = &ipstats_stat_desc_pack_cpu_hit,
+	.show = &ipstats_stat_desc_show_cpu_hit,
+};
+
+static const struct ipstats_stat_desc *ipstats_stat_desc_offload_subs[] = {
+	&ipstats_stat_desc_offload_cpu_hit,
+};
+
+static const struct ipstats_stat_desc ipstats_stat_desc_offload_group = {
+	.name = "offload",
+	.kind = IPSTATS_STAT_DESC_KIND_GROUP,
+	.subs = ipstats_stat_desc_offload_subs,
+	.nsubs = ARRAY_SIZE(ipstats_stat_desc_offload_subs),
+};
+
 static void
 ipstats_stat_desc_pack_link(struct ipstats_stat_dump_filters *filters,
 			    const struct ipstats_stat_desc *desc)
@@ -189,6 +225,7 @@ static const struct ipstats_stat_desc ipstats_stat_desc_toplev_link = {
 
 static const struct ipstats_stat_desc *ipstats_stat_desc_toplev_subs[] = {
 	&ipstats_stat_desc_toplev_link,
+	&ipstats_stat_desc_offload_group,
 };
 
 static const struct ipstats_stat_desc ipstats_stat_desc_toplev_group = {
-- 
2.31.1


^ permalink raw reply related

* [PATCH iproute2-next 05/11] ipstats: Add a shell of "show" command
From: Petr Machata @ 2022-04-22  8:30 UTC (permalink / raw)
  To: netdev; +Cc: David Ahern, Stephen Hemminger, Ido Schimmel, Petr Machata
In-Reply-To: <cover.1650615982.git.petrm@nvidia.com>

Add the scaffolding necessary for adding individual stats suites to show.

Expose some ipstats artifacts in ip_common.h. These will be used to support
"xstats" in a follow-up patchset.

Signed-off-by: Petr Machata <petrm@nvidia.com>
Reviewed-by: Ido Schimmel <idosch@nvidia.com>
---
 ip/ip_common.h |  26 +++
 ip/ipstats.c   | 614 ++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 638 insertions(+), 2 deletions(-)

diff --git a/ip/ip_common.h b/ip/ip_common.h
index 53866d7a..8b0a6426 100644
--- a/ip/ip_common.h
+++ b/ip/ip_common.h
@@ -158,6 +158,32 @@ void xdp_dump(FILE *fp, struct rtattr *tb, bool link, bool details);
 __u32 ipvrf_get_table(const char *name);
 int name_is_vrf(const char *name);
 
+/* ipstats.c */
+enum ipstats_stat_desc_kind {
+	IPSTATS_STAT_DESC_KIND_LEAF,
+	IPSTATS_STAT_DESC_KIND_GROUP,
+};
+
+struct ipstats_stat_dump_filters;
+struct ipstats_stat_show_attrs;
+
+struct ipstats_stat_desc {
+	const char *name;
+	enum ipstats_stat_desc_kind kind;
+	union {
+		struct {
+			const struct ipstats_stat_desc **subs;
+			size_t nsubs;
+		};
+		struct {
+			void (*pack)(struct ipstats_stat_dump_filters *filters,
+				     const struct ipstats_stat_desc *desc);
+			int (*show)(struct ipstats_stat_show_attrs *attrs,
+				    const struct ipstats_stat_desc *desc);
+		};
+	};
+};
+
 #ifndef	INFINITY_LIFE_TIME
 #define     INFINITY_LIFE_TIME      0xFFFFFFFFU
 #endif
diff --git a/ip/ipstats.c b/ip/ipstats.c
index 1f5b3f77..79f7b1ff 100644
--- a/ip/ipstats.c
+++ b/ip/ipstats.c
@@ -1,19 +1,624 @@
 // SPDX-License-Identifier: GPL-2.0+
+#include <assert.h>
 #include <errno.h>
 
 #include "utils.h"
 #include "ip_common.h"
 
+struct ipstats_stat_dump_filters {
+	/* mask[0] filters outer attributes. Then individual nests have their
+	 * filtering mask at the index of the nested attribute.
+	 */
+	__u32 mask[IFLA_STATS_MAX + 1];
+};
+
+struct ipstats_stat_show_attrs {
+	struct if_stats_msg *ifsm;
+	int len;
+
+	/* tbs[0] contains top-level attribute table. Then individual nests have
+	 * their attribute tables at the index of the nested attribute.
+	 */
+	struct rtattr **tbs[IFLA_STATS_MAX + 1];
+};
+
+static const char *const ipstats_levels[] = {
+	"group",
+	"subgroup",
+};
+
+enum {
+	IPSTATS_LEVELS_COUNT = ARRAY_SIZE(ipstats_levels),
+};
+
+struct ipstats_sel {
+	const char *sel[IPSTATS_LEVELS_COUNT];
+};
+
+struct ipstats_stat_enabled_one {
+	const struct ipstats_stat_desc *desc;
+	struct ipstats_sel sel;
+};
+
+struct ipstats_stat_enabled {
+	struct ipstats_stat_enabled_one *enabled;
+	size_t nenabled;
+};
+
+static const unsigned int ipstats_stat_ifla_max[] = {
+	[0] = IFLA_STATS_MAX,
+	[IFLA_STATS_LINK_XSTATS] = LINK_XSTATS_TYPE_MAX,
+	[IFLA_STATS_LINK_XSTATS_SLAVE] = LINK_XSTATS_TYPE_MAX,
+	[IFLA_STATS_LINK_OFFLOAD_XSTATS] = IFLA_OFFLOAD_XSTATS_MAX,
+	[IFLA_STATS_AF_SPEC] = AF_MAX - 1,
+};
+
+static_assert(ARRAY_SIZE(ipstats_stat_ifla_max) == IFLA_STATS_MAX + 1,
+	      "An IFLA_STATS attribute is missing from the ifla_max table");
+
+static int
+ipstats_stat_show_attrs_alloc_tb(struct ipstats_stat_show_attrs *attrs,
+				 unsigned int group)
+{
+	unsigned int ifla_max;
+	int err;
+
+	assert(group < ARRAY_SIZE(ipstats_stat_ifla_max));
+	assert(group < ARRAY_SIZE(attrs->tbs));
+	ifla_max = ipstats_stat_ifla_max[group];
+	assert(ifla_max != 0);
+
+	if (attrs->tbs[group])
+		return 0;
+
+	attrs->tbs[group] = calloc(ifla_max + 1, sizeof(*attrs->tbs[group]));
+	if (attrs->tbs[group] == NULL)
+		return -ENOMEM;
+
+	if (group == 0)
+		err = parse_rtattr(attrs->tbs[group], ifla_max,
+				   IFLA_STATS_RTA(attrs->ifsm), attrs->len);
+	else
+		err = parse_rtattr_nested(attrs->tbs[group], ifla_max,
+					  attrs->tbs[0][group]);
+
+	if (err != 0) {
+		free(attrs->tbs[group]);
+		attrs->tbs[group] = NULL;
+	}
+	return err;
+}
+
+static void
+ipstats_stat_show_attrs_free(struct ipstats_stat_show_attrs *attrs)
+{
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(attrs->tbs); i++)
+		free(attrs->tbs[i]);
+}
+
+static const struct ipstats_stat_desc *ipstats_stat_desc_toplev_subs[] = {
+};
+
+static const struct ipstats_stat_desc ipstats_stat_desc_toplev_group = {
+	.name = "top-level",
+	.kind = IPSTATS_STAT_DESC_KIND_GROUP,
+	.subs = ipstats_stat_desc_toplev_subs,
+	.nsubs = ARRAY_SIZE(ipstats_stat_desc_toplev_subs),
+};
+
+static void ipstats_show_group(const struct ipstats_sel *sel)
+{
+	int i;
+
+	for (i = 0; i < IPSTATS_LEVELS_COUNT; i++) {
+		if (sel->sel[i] == NULL)
+			break;
+		print_string(PRINT_JSON, ipstats_levels[i], NULL, sel->sel[i]);
+		print_string(PRINT_FP, NULL, " %s ", ipstats_levels[i]);
+		print_string(PRINT_FP, NULL, "%s", sel->sel[i]);
+	}
+}
+
+static int
+ipstats_process_ifsm(struct nlmsghdr *answer,
+		     struct ipstats_stat_enabled *enabled)
+{
+	struct ipstats_stat_show_attrs show_attrs = {};
+	const char *dev;
+	int err = 0;
+	int i;
+
+	show_attrs.ifsm = NLMSG_DATA(answer);
+	show_attrs.len = (answer->nlmsg_len -
+			  NLMSG_LENGTH(sizeof(*show_attrs.ifsm)));
+	if (show_attrs.len < 0) {
+		fprintf(stderr, "BUG: wrong nlmsg len %d\n", show_attrs.len);
+		return -EINVAL;
+	}
+
+	err = ipstats_stat_show_attrs_alloc_tb(&show_attrs, 0);
+	if (err != 0) {
+		fprintf(stderr, "Error parsing netlink answer: %s\n",
+			strerror(err));
+		return err;
+	}
+
+	dev = ll_index_to_name(show_attrs.ifsm->ifindex);
+
+	for (i = 0; i < enabled->nenabled; i++) {
+		const struct ipstats_stat_desc *desc = enabled->enabled[i].desc;
+
+		open_json_object(NULL);
+		print_int(PRINT_ANY, "ifindex", "%d:",
+			  show_attrs.ifsm->ifindex);
+		print_color_string(PRINT_ANY, COLOR_IFNAME,
+				   "ifname", " %s:", dev);
+		ipstats_show_group(&enabled->enabled[i].sel);
+		err = desc->show(&show_attrs, desc);
+		if (err != 0)
+			goto out;
+		close_json_object();
+		print_nl();
+	}
+
+out:
+	ipstats_stat_show_attrs_free(&show_attrs);
+	return err;
+}
+
+static bool
+ipstats_req_should_filter_at(struct ipstats_stat_dump_filters *filters, int at)
+{
+	return filters->mask[at] != 0 &&
+	       filters->mask[at] != (1 << ipstats_stat_ifla_max[at]) - 1;
+}
+
+static int
+ipstats_req_add_filters(struct ipstats_req *req, void *data)
+{
+	struct ipstats_stat_dump_filters dump_filters = {};
+	struct ipstats_stat_enabled *enabled = data;
+	bool get_filters = false;
+	int i;
+
+	for (i = 0; i < enabled->nenabled; i++)
+		enabled->enabled[i].desc->pack(&dump_filters,
+					       enabled->enabled[i].desc);
+
+	for (i = 1; i < ARRAY_SIZE(dump_filters.mask); i++) {
+		if (ipstats_req_should_filter_at(&dump_filters, i)) {
+			get_filters = true;
+			break;
+		}
+	}
+
+	req->ifsm.filter_mask = dump_filters.mask[0];
+	if (get_filters) {
+		struct rtattr *nest;
+
+		nest = addattr_nest(&req->nlh, sizeof(*req),
+				    IFLA_STATS_GET_FILTERS | NLA_F_NESTED);
+
+		for (i = 1; i < ARRAY_SIZE(dump_filters.mask); i++) {
+			if (ipstats_req_should_filter_at(&dump_filters, i))
+				addattr32(&req->nlh, sizeof(*req), i,
+					  dump_filters.mask[i]);
+		}
+
+		addattr_nest_end(&req->nlh, nest);
+	}
+
+	return 0;
+}
+
+static int
+ipstats_show_one(int ifindex, struct ipstats_stat_enabled *enabled)
+{
+	struct ipstats_req req = {
+		.nlh.nlmsg_flags = NLM_F_REQUEST,
+		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct if_stats_msg)),
+		.nlh.nlmsg_type = RTM_GETSTATS,
+		.ifsm.family = PF_UNSPEC,
+		.ifsm.ifindex = ifindex,
+	};
+	struct nlmsghdr *answer;
+	int err = 0;
+
+	ipstats_req_add_filters(&req, enabled);
+	if (rtnl_talk(&rth, &req.nlh, &answer) < 0)
+		return -2;
+	err = ipstats_process_ifsm(answer, enabled);
+	free(answer);
+
+	return err;
+}
+
+static int ipstats_dump_one(struct nlmsghdr *n, void *arg)
+{
+	struct ipstats_stat_enabled *enabled = arg;
+	int rc;
+
+	rc = ipstats_process_ifsm(n, enabled);
+	if (rc)
+		return rc;
+
+	print_nl();
+	return 0;
+}
+
+static int ipstats_dump(struct ipstats_stat_enabled *enabled)
+{
+	int rc = 0;
+
+	if (rtnl_statsdump_req_filter(&rth, PF_UNSPEC, 0,
+				      ipstats_req_add_filters,
+				      enabled) < 0) {
+		perror("Cannot send dump request");
+		return -2;
+	}
+
+	if (rtnl_dump_filter(&rth, ipstats_dump_one, enabled) < 0) {
+		fprintf(stderr, "Dump terminated\n");
+		rc = -2;
+	}
+
+	fflush(stdout);
+	return rc;
+}
+
+static int
+ipstats_show_do(int ifindex, struct ipstats_stat_enabled *enabled)
+{
+	int rc;
+
+	new_json_obj(json);
+	if (ifindex)
+		rc = ipstats_show_one(ifindex, enabled);
+	else
+		rc = ipstats_dump(enabled);
+	delete_json_obj();
+
+	return rc;
+}
+
+static int ipstats_add_enabled(struct ipstats_stat_enabled_one ens[],
+			       size_t nens,
+			       struct ipstats_stat_enabled *enabled)
+{
+	struct ipstats_stat_enabled_one *new_en;
+
+	new_en = realloc(enabled->enabled,
+			 sizeof(*new_en) * (enabled->nenabled + nens));
+	if (new_en == NULL)
+		return -ENOMEM;
+
+	enabled->enabled = new_en;
+	while (nens-- > 0)
+		enabled->enabled[enabled->nenabled++] = *ens++;
+	return 0;
+}
+
+static void ipstats_select_push(struct ipstats_sel *sel, const char *name)
+{
+	int i;
+
+	for (i = 0; i < IPSTATS_LEVELS_COUNT; i++)
+		if (sel->sel[i] == NULL) {
+			sel->sel[i] = name;
+			return;
+		}
+
+	assert(false);
+}
+
+static int
+ipstats_enable_recursively(const struct ipstats_stat_desc *desc,
+			   struct ipstats_stat_enabled *enabled,
+			   const struct ipstats_sel *sel)
+{
+	bool found = false;
+	size_t i;
+	int err;
+
+	if (desc->kind == IPSTATS_STAT_DESC_KIND_LEAF) {
+		struct ipstats_stat_enabled_one en[] = {{
+			.desc = desc,
+			.sel = *sel,
+		}};
+
+		return ipstats_add_enabled(en, ARRAY_SIZE(en), enabled);
+	}
+
+	for (i = 0; i < desc->nsubs; i++) {
+		struct ipstats_sel subsel = *sel;
+
+		ipstats_select_push(&subsel, desc->subs[i]->name);
+		err = ipstats_enable_recursively(desc->subs[i], enabled,
+						 &subsel);
+		if (err == -ENOENT)
+			continue;
+		if (err != 0)
+			return err;
+		found = true;
+	}
+
+	return found ? 0 : -ENOENT;
+}
+
+static int ipstats_comp_enabled(const void *a, const void *b)
+{
+	const struct ipstats_stat_enabled_one *en_a = a;
+	const struct ipstats_stat_enabled_one *en_b = b;
+
+	if (en_a->desc < en_b->desc)
+		return -1;
+	if (en_a->desc > en_b->desc)
+		return 1;
+
+	return 0;
+}
+
+static void ipstats_enabled_free(struct ipstats_stat_enabled *enabled)
+{
+	free(enabled->enabled);
+}
+
+static const struct ipstats_stat_desc *
+ipstats_stat_desc_find(const struct ipstats_stat_desc *desc,
+		       const char *name)
+{
+	size_t i;
+
+	assert(desc->kind == IPSTATS_STAT_DESC_KIND_GROUP);
+	for (i = 0; i < desc->nsubs; i++) {
+		const struct ipstats_stat_desc *sub = desc->subs[i];
+
+		if (strcmp(sub->name, name) == 0)
+			return sub;
+	}
+
+	return NULL;
+}
+
+static const struct ipstats_stat_desc *
+ipstats_enable_find_stat_desc(struct ipstats_sel *sel)
+{
+	const struct ipstats_stat_desc *toplev = &ipstats_stat_desc_toplev_group;
+	const struct ipstats_stat_desc *desc = toplev;
+	int i;
+
+	for (i = 0; i < IPSTATS_LEVELS_COUNT; i++) {
+		const struct ipstats_stat_desc *next_desc;
+
+		if (sel->sel[i] == NULL)
+			break;
+		if (desc->kind == IPSTATS_STAT_DESC_KIND_LEAF) {
+			fprintf(stderr, "Error: %s %s requested inside leaf %s %s\n",
+				ipstats_levels[i], sel->sel[i],
+				ipstats_levels[i - 1], desc->name);
+			return NULL;
+		}
+
+		next_desc = ipstats_stat_desc_find(desc, sel->sel[i]);
+		if (next_desc == NULL) {
+			fprintf(stderr, "Error: no %s named %s found inside %s\n",
+				ipstats_levels[i], sel->sel[i], desc->name);
+			return NULL;
+		}
+
+		desc = next_desc;
+	}
+
+	return desc;
+}
+
+static int ipstats_enable(struct ipstats_sel *sel,
+			  struct ipstats_stat_enabled *enabled)
+{
+	struct ipstats_stat_enabled new_enabled = {};
+	const struct ipstats_stat_desc *desc;
+	size_t i, j;
+	int err = 0;
+
+	desc = ipstats_enable_find_stat_desc(sel);
+	if (desc == NULL)
+		return -EINVAL;
+
+	err = ipstats_enable_recursively(desc, &new_enabled, sel);
+	if (err != 0)
+		return err;
+
+	err = ipstats_add_enabled(new_enabled.enabled, new_enabled.nenabled,
+				  enabled);
+	if (err != 0)
+		goto out;
+
+	qsort(enabled->enabled, enabled->nenabled, sizeof(*enabled->enabled),
+	      ipstats_comp_enabled);
+
+	for (i = 1, j = 1; i < enabled->nenabled; i++) {
+		if (enabled->enabled[i].desc != enabled->enabled[j - 1].desc)
+			enabled->enabled[j++] = enabled->enabled[i];
+	}
+	enabled->nenabled = j;
+
+out:
+	ipstats_enabled_free(&new_enabled);
+	return err;
+}
+
+static int ipstats_enable_check(struct ipstats_sel *sel,
+				struct ipstats_stat_enabled *enabled)
+{
+	int err;
+	int i;
+
+	err = ipstats_enable(sel, enabled);
+	if (err == -ENOENT) {
+		fprintf(stderr, "The request for");
+		for (i = 0; i < IPSTATS_LEVELS_COUNT; i++)
+			if (sel->sel[i] != NULL)
+				fprintf(stderr, " %s %s",
+					ipstats_levels[i], sel->sel[i]);
+			else
+				break;
+		fprintf(stderr, " did not match any known stats.\n");
+	}
+
+	return err;
+}
+
 static int do_help(void)
 {
+	const struct ipstats_stat_desc *toplev = &ipstats_stat_desc_toplev_group;
+	int i;
+
 	fprintf(stderr,
 		"Usage: ip stats help\n"
+		"       ip stats show [ dev DEV ] [ group GROUP [ subgroup SUBGROUP ] ... ] ...\n"
 		"       ip stats set dev DEV l3_stats { on | off }\n"
 		);
 
+	for (i = 0; i < toplev->nsubs; i++) {
+		const struct ipstats_stat_desc *desc = toplev->subs[i];
+
+		if (i == 0)
+			fprintf(stderr, "GROUP := { %s", desc->name);
+		else
+			fprintf(stderr, " | %s", desc->name);
+	}
+	if (i > 0)
+		fprintf(stderr, " }\n");
+
+	for (i = 0; i < toplev->nsubs; i++) {
+		const struct ipstats_stat_desc *desc = toplev->subs[i];
+		bool opened = false;
+		size_t j;
+
+		if (desc->kind != IPSTATS_STAT_DESC_KIND_GROUP)
+			continue;
+
+		for (j = 0; j < desc->nsubs; j++) {
+			if (j == 0)
+				fprintf(stderr, "%s SUBGROUP := {", desc->name);
+			else
+				fprintf(stderr, " |");
+			fprintf(stderr, " %s", desc->subs[j]->name);
+			opened = true;
+
+			if (desc->subs[j]->kind != IPSTATS_STAT_DESC_KIND_GROUP)
+				continue;
+		}
+		if (opened)
+			fprintf(stderr, " }\n");
+	}
+
 	return 0;
 }
 
+static int ipstats_select(struct ipstats_sel *old_sel,
+			  const char *new_sel, int level,
+			  struct ipstats_stat_enabled *enabled)
+{
+	int err;
+	int i;
+
+	for (i = 0; i < level; i++) {
+		if (old_sel->sel[i] == NULL) {
+			fprintf(stderr, "Error: %s %s requested without selecting a %s first\n",
+				ipstats_levels[level], new_sel,
+				ipstats_levels[i]);
+			return -EINVAL;
+		}
+	}
+
+	for (i = level; i < IPSTATS_LEVELS_COUNT; i++) {
+		if (old_sel->sel[i] != NULL) {
+			err = ipstats_enable_check(old_sel, enabled);
+			if (err)
+				return err;
+			break;
+		}
+	}
+
+	old_sel->sel[level] = new_sel;
+	for (i = level + 1; i < IPSTATS_LEVELS_COUNT; i++)
+		old_sel->sel[i] = NULL;
+
+	return 0;
+}
+
+static int ipstats_show(int argc, char **argv)
+{
+	struct ipstats_stat_enabled enabled = {};
+	struct ipstats_sel sel = {};
+	const char *dev = NULL;
+	int ifindex;
+	int err;
+	int i;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "dev") == 0) {
+			NEXT_ARG();
+			if (dev != NULL)
+				duparg2("dev", *argv);
+			if (check_ifname(*argv))
+				invarg("\"dev\" not a valid ifname", *argv);
+			dev = *argv;
+		} else if (strcmp(*argv, "help") == 0) {
+			do_help();
+			return 0;
+		} else {
+			bool found_level = false;
+
+			for (i = 0; i < ARRAY_SIZE(ipstats_levels); i++) {
+				if (strcmp(*argv, ipstats_levels[i]) == 0) {
+					NEXT_ARG();
+					err = ipstats_select(&sel, *argv, i,
+							     &enabled);
+					if (err)
+						goto err;
+
+					found_level = true;
+				}
+			}
+
+			if (!found_level) {
+				fprintf(stderr, "What is \"%s\"?\n", *argv);
+				do_help();
+				err = -EINVAL;
+				goto err;
+			}
+		}
+
+		NEXT_ARG_FWD();
+	}
+
+	/* Push whatever was given. */
+	err = ipstats_enable_check(&sel, &enabled);
+	if (err)
+		goto err;
+
+	if (dev) {
+		ifindex = ll_name_to_index(dev);
+		if (!ifindex) {
+			err = nodev(dev);
+			goto err;
+		}
+	} else {
+		ifindex = 0;
+	}
+
+
+	err = ipstats_show_do(ifindex, &enabled);
+
+err:
+	ipstats_enabled_free(&enabled);
+	return err;
+}
+
 static int ipstats_set_do(int ifindex, int at, bool enable)
 {
 	struct ipstats_req req = {
@@ -92,11 +697,16 @@ int do_ipstats(int argc, char **argv)
 	int rc;
 
 	if (argc == 0) {
-		do_help();
-		rc = -1;
+		rc = ipstats_show(0, NULL);
 	} else if (strcmp(*argv, "help") == 0) {
 		do_help();
 		rc = 0;
+	} else if (strcmp(*argv, "show") == 0) {
+		/* Invoking "stats show" implies one -s. Passing -d adds one
+		 * more -s.
+		 */
+		show_stats += show_details + 1;
+		rc = ipstats_show(argc-1, argv+1);
 	} else if (strcmp(*argv, "set") == 0) {
 		rc = ipstats_set(argc-1, argv+1);
 	} else {
-- 
2.31.1


^ permalink raw reply related

* [PATCH iproute2-next 06/11] ipstats: Add a group "link"
From: Petr Machata @ 2022-04-22  8:30 UTC (permalink / raw)
  To: netdev; +Cc: David Ahern, Stephen Hemminger, Ido Schimmel, Petr Machata
In-Reply-To: <cover.1650615982.git.petrm@nvidia.com>

Add the "link" top-level group for showing IFLA_STATS_LINK_64 statistics.
For example:

 # ip stats show dev swp1 group link
 4178: swp1: group link
     RX:  bytes packets errors dropped  missed   mcast
          47048     354      0       0       0      64
     TX:  bytes packets errors dropped carrier collsns
          47474     355      0       0       0       0

 # ip -j stats show dev swp1 group link | jq
 [
   {
     "ifindex": 4178,
     "ifname": "swp1",
     "group": "link",
     "stats64": {
       "rx": {
         "bytes": 47048,
         "packets": 354,
         "errors": 0,
         "dropped": 0,
         "over_errors": 0,
         "multicast": 64
       },
       "tx": {
         "bytes": 47474,
         "packets": 355,
         "errors": 0,
         "dropped": 0,
         "carrier_errors": 0,
         "collisions": 0
       }
     }
   }
 ]

Signed-off-by: Petr Machata <petrm@nvidia.com>
Reviewed-by: Ido Schimmel <idosch@nvidia.com>
---
 ip/ipstats.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 90 insertions(+)

diff --git a/ip/ipstats.c b/ip/ipstats.c
index 79f7b1ff..e4f97ddd 100644
--- a/ip/ipstats.c
+++ b/ip/ipstats.c
@@ -12,6 +12,15 @@ struct ipstats_stat_dump_filters {
 	__u32 mask[IFLA_STATS_MAX + 1];
 };
 
+static void
+ipstats_stat_desc_enable_bit(struct ipstats_stat_dump_filters *filters,
+			     unsigned int group, unsigned int subgroup)
+{
+	filters->mask[0] |= IFLA_STATS_FILTER_BIT(group);
+	if (subgroup)
+		filters->mask[group] |= IFLA_STATS_FILTER_BIT(subgroup);
+}
+
 struct ipstats_stat_show_attrs {
 	struct if_stats_msg *ifsm;
 	int len;
@@ -89,6 +98,29 @@ ipstats_stat_show_attrs_alloc_tb(struct ipstats_stat_show_attrs *attrs,
 	return err;
 }
 
+static const struct rtattr *
+ipstats_stat_show_get_attr(struct ipstats_stat_show_attrs *attrs,
+			   int group, int subgroup, int *err)
+{
+	int tmp_err;
+
+	if (err == NULL)
+		err = &tmp_err;
+
+	*err = 0;
+	if (subgroup == 0)
+		return attrs->tbs[0][group];
+
+	if (attrs->tbs[0][group] == NULL)
+		return NULL;
+
+	*err = ipstats_stat_show_attrs_alloc_tb(attrs, group);
+	if (*err != 0)
+		return NULL;
+
+	return attrs->tbs[group][subgroup];
+}
+
 static void
 ipstats_stat_show_attrs_free(struct ipstats_stat_show_attrs *attrs)
 {
@@ -98,7 +130,65 @@ ipstats_stat_show_attrs_free(struct ipstats_stat_show_attrs *attrs)
 		free(attrs->tbs[i]);
 }
 
+#define IPSTATS_RTA_PAYLOAD(TYPE, AT)					\
+	({								\
+		const struct rtattr *__at = (AT);			\
+		TYPE *__ret = NULL;					\
+									\
+		if (__at != NULL &&					\
+		    __at->rta_len - RTA_LENGTH(0) >= sizeof(TYPE))	\
+			__ret = RTA_DATA(__at);				\
+		__ret;							\
+	})
+
+static int ipstats_show_64(struct ipstats_stat_show_attrs *attrs,
+			   unsigned int group, unsigned int subgroup)
+{
+	struct rtnl_link_stats64 *stats;
+	const struct rtattr *at;
+	int err;
+
+	at = ipstats_stat_show_get_attr(attrs, group, subgroup, &err);
+	if (at == NULL)
+		return err;
+
+	stats = IPSTATS_RTA_PAYLOAD(struct rtnl_link_stats64, at);
+	if (stats == NULL) {
+		fprintf(stderr, "Error: attribute payload too short");
+		return -EINVAL;
+	}
+
+	open_json_object("stats64");
+	print_stats64(stdout, stats, NULL, NULL);
+	close_json_object();
+	return 0;
+}
+
+static void
+ipstats_stat_desc_pack_link(struct ipstats_stat_dump_filters *filters,
+			    const struct ipstats_stat_desc *desc)
+{
+	ipstats_stat_desc_enable_bit(filters,
+				     IFLA_STATS_LINK_64, 0);
+}
+
+static int
+ipstats_stat_desc_show_link(struct ipstats_stat_show_attrs *attrs,
+			    const struct ipstats_stat_desc *desc)
+{
+	print_nl();
+	return ipstats_show_64(attrs, IFLA_STATS_LINK_64, 0);
+}
+
+static const struct ipstats_stat_desc ipstats_stat_desc_toplev_link = {
+	.name = "link",
+	.kind = IPSTATS_STAT_DESC_KIND_LEAF,
+	.pack = &ipstats_stat_desc_pack_link,
+	.show = &ipstats_stat_desc_show_link,
+};
+
 static const struct ipstats_stat_desc *ipstats_stat_desc_toplev_subs[] = {
+	&ipstats_stat_desc_toplev_link,
 };
 
 static const struct ipstats_stat_desc ipstats_stat_desc_toplev_group = {
-- 
2.31.1


^ permalink raw reply related

* [PATCH iproute2-next 10/11] ipmonitor: Add monitoring support for stats events
From: Petr Machata @ 2022-04-22  8:30 UTC (permalink / raw)
  To: netdev; +Cc: David Ahern, Stephen Hemminger, Ido Schimmel, Petr Machata
In-Reply-To: <cover.1650615982.git.petrm@nvidia.com>

Toggles and offloads of HW statistics cause emission of and RTM_NEWSTATS
event. Add support to "ip monitor" for these events.

Signed-off-by: Petr Machata <petrm@nvidia.com>
Reviewed-by: Ido Schimmel <idosch@nvidia.com>
---
 ip/ip_common.h |  1 +
 ip/ipmonitor.c | 16 +++++++++++++++-
 ip/ipstats.c   | 20 ++++++++++++++++++++
 3 files changed, 36 insertions(+), 1 deletion(-)

diff --git a/ip/ip_common.h b/ip/ip_common.h
index 8b0a6426..9eeeb387 100644
--- a/ip/ip_common.h
+++ b/ip/ip_common.h
@@ -57,6 +57,7 @@ int print_nexthop_bucket(struct nlmsghdr *n, void *arg);
 void netns_map_init(void);
 void netns_nsid_socket_init(void);
 int print_nsid(struct nlmsghdr *n, void *arg);
+int ipstats_print(struct nlmsghdr *n, void *arg);
 char *get_name_from_nsid(int nsid);
 int get_netnsid_from_name(const char *name);
 int set_netnsid_from_name(const char *name, int nsid);
diff --git a/ip/ipmonitor.c b/ip/ipmonitor.c
index 0badda4e..a4326d2a 100644
--- a/ip/ipmonitor.c
+++ b/ip/ipmonitor.c
@@ -34,7 +34,7 @@ static void usage(void)
 		"Usage: ip monitor [ all | OBJECTS ] [ FILE ] [ label ] [ all-nsid ]\n"
 		"                  [ dev DEVICE ]\n"
 		"OBJECTS :=  address | link | mroute | neigh | netconf |\n"
-		"            nexthop | nsid | prefix | route | rule\n"
+		"            nexthop | nsid | prefix | route | rule | stats\n"
 		"FILE := file FILENAME\n");
 	exit(-1);
 }
@@ -158,6 +158,11 @@ static int accept_msg(struct rtnl_ctrl_data *ctrl,
 		print_nsid(n, arg);
 		return 0;
 
+	case RTM_NEWSTATS:
+		print_headers(fp, "[STATS]", ctrl);
+		ipstats_print(n, arg);
+		return 0;
+
 	case NLMSG_ERROR:
 	case NLMSG_NOOP:
 	case NLMSG_DONE:
@@ -185,6 +190,7 @@ int do_ipmonitor(int argc, char **argv)
 	int lprefix = 0;
 	int lneigh = 0;
 	int lnetconf = 0;
+	int lstats = 0;
 	int lrule = 0;
 	int lnsid = 0;
 	int ifindex = 0;
@@ -253,6 +259,9 @@ int do_ipmonitor(int argc, char **argv)
 		} else if (matches(*argv, "nexthop") == 0) {
 			lnexthop = 1;
 			groups = 0;
+		} else if (strcmp(*argv, "stats") == 0) {
+			lstats = 1;
+			groups = 0;
 		} else if (strcmp(*argv, "all") == 0) {
 			prefix_banner = 1;
 		} else if (matches(*argv, "all-nsid") == 0) {
@@ -349,6 +358,11 @@ int do_ipmonitor(int argc, char **argv)
 		exit(1);
 	}
 
+	if (lstats && rtnl_add_nl_group(&rth, RTNLGRP_STATS) < 0) {
+		fprintf(stderr, "Failed to add stats group to list\n");
+		exit(1);
+	}
+
 	if (listen_all_nsid && rtnl_listen_all_nsid(&rth) < 0)
 		exit(1);
 
diff --git a/ip/ipstats.c b/ip/ipstats.c
index 29ca0731..39ddca01 100644
--- a/ip/ipstats.c
+++ b/ip/ipstats.c
@@ -1219,3 +1219,23 @@ int do_ipstats(int argc, char **argv)
 
 	return rc;
 }
+
+int ipstats_print(struct nlmsghdr *n, void *arg)
+{
+	struct ipstats_stat_enabled_one one = {
+		.desc = &ipstats_stat_desc_offload_hw_s_info,
+	};
+	struct ipstats_stat_enabled enabled = {
+		.enabled = &one,
+		.nenabled = 1,
+	};
+	FILE *fp = arg;
+	int rc;
+
+	rc = ipstats_process_ifsm(n, &enabled);
+	if (rc)
+		return rc;
+
+	fflush(fp);
+	return 0;
+}
-- 
2.31.1


^ permalink raw reply related

* [PATCH iproute2-next 08/11] ipstats: Add offload subgroup "hw_stats_info"
From: Petr Machata @ 2022-04-22  8:30 UTC (permalink / raw)
  To: netdev; +Cc: David Ahern, Stephen Hemminger, Ido Schimmel, Petr Machata
In-Reply-To: <cover.1650615982.git.petrm@nvidia.com>

Add into the group "offload" a subgroup "hw_stats_info" for showing
information about HW statistics counters.

For example:

 # ip stats show dev swp1 group offload subgroup hw_stats_info
 4178: swp1: group offload subgroup hw_stats_info
     l3_stats on used off
 # ip -j stats show dev swp1 group offload subgroup hw_stats_info | jq
 [
   {
     "ifindex": 4178,
     "ifname": "swp1",
     "group": "offload",
     "subgroup": "hw_stats_info",
     "info": {
       "l3_stats": {
         "request": true,
         "used": false
       }
     }
   }
 ]

Signed-off-by: Petr Machata <petrm@nvidia.com>
Reviewed-by: Ido Schimmel <idosch@nvidia.com>
---
 ip/ipstats.c | 217 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 217 insertions(+)

diff --git a/ip/ipstats.c b/ip/ipstats.c
index 283fba4e..4cdd5122 100644
--- a/ip/ipstats.c
+++ b/ip/ipstats.c
@@ -164,6 +164,197 @@ static int ipstats_show_64(struct ipstats_stat_show_attrs *attrs,
 	return 0;
 }
 
+enum ipstats_maybe_on_off {
+	IPSTATS_MOO_OFF = -1,
+	IPSTATS_MOO_INVALID,
+	IPSTATS_MOO_ON,
+};
+
+static bool ipstats_moo_to_bool(enum ipstats_maybe_on_off moo)
+{
+	assert(moo != IPSTATS_MOO_INVALID);
+	return moo + 1;
+}
+
+static int ipstats_print_moo(enum output_type t, const char *key,
+			     const char *fmt, enum ipstats_maybe_on_off moo)
+{
+	if (!moo)
+		return 0;
+	return print_on_off(t, key, fmt, ipstats_moo_to_bool(moo));
+}
+
+struct ipstats_hw_s_info_one {
+	enum ipstats_maybe_on_off request;
+	enum ipstats_maybe_on_off used;
+};
+
+enum ipstats_hw_s_info_idx {
+	IPSTATS_HW_S_INFO_IDX_L3_STATS,
+	IPSTATS_HW_S_INFO_IDX_COUNT
+};
+
+static const char *const ipstats_hw_s_info_name[] = {
+	"l3_stats",
+};
+
+static_assert(ARRAY_SIZE(ipstats_hw_s_info_name) ==
+	      IPSTATS_HW_S_INFO_IDX_COUNT,
+	      "mismatch: enum ipstats_hw_s_info_idx x ipstats_hw_s_info_name");
+
+struct ipstats_hw_s_info {
+	/* Indexed by enum ipstats_hw_s_info_idx. */
+	struct ipstats_hw_s_info_one *infos[IPSTATS_HW_S_INFO_IDX_COUNT];
+};
+
+static enum ipstats_maybe_on_off ipstats_dissect_01(int value, const char *what)
+{
+	switch (value) {
+	case 0:
+		return IPSTATS_MOO_OFF;
+	case 1:
+		return IPSTATS_MOO_ON;
+	default:
+		fprintf(stderr, "Invalid value for %s: expected 0 or 1, got %d.\n",
+			what, value);
+		return IPSTATS_MOO_INVALID;
+	}
+}
+
+static int ipstats_dissect_hw_s_info_one(const struct rtattr *at,
+					 struct ipstats_hw_s_info_one *p_hwsio,
+					 const char *what)
+{
+	int attr_id_request = IFLA_OFFLOAD_XSTATS_HW_S_INFO_REQUEST;
+	struct rtattr *tb[IFLA_OFFLOAD_XSTATS_HW_S_INFO_MAX + 1];
+	int attr_id_used = IFLA_OFFLOAD_XSTATS_HW_S_INFO_USED;
+	struct ipstats_hw_s_info_one hwsio = {};
+	int err;
+	int v;
+
+	err = parse_rtattr_nested(tb, IFLA_OFFLOAD_XSTATS_HW_S_INFO_MAX, at);
+	if (err)
+		return err;
+
+	if (tb[attr_id_request]) {
+		v = rta_getattr_u8(tb[attr_id_request]);
+		hwsio.request = ipstats_dissect_01(v, "request");
+
+		/* This has to be present & valid. */
+		if (!hwsio.request)
+			return -EINVAL;
+	}
+
+	if (tb[attr_id_used]) {
+		v = rta_getattr_u8(tb[attr_id_used]);
+		hwsio.used = ipstats_dissect_01(v, "used");
+	}
+
+	*p_hwsio = hwsio;
+	return 0;
+}
+
+static int ipstats_dissect_hw_s_info(const struct rtattr *at,
+				     struct ipstats_hw_s_info *hwsi)
+{
+	struct rtattr *tb[IFLA_OFFLOAD_XSTATS_MAX + 1];
+	int attr_id_l3 = IFLA_OFFLOAD_XSTATS_L3_STATS;
+	struct ipstats_hw_s_info_one *hwsio = NULL;
+	int err;
+
+	err = parse_rtattr_nested(tb, IFLA_OFFLOAD_XSTATS_MAX, at);
+	if (err)
+		return err;
+
+	*hwsi = (struct ipstats_hw_s_info){};
+
+	if (tb[attr_id_l3]) {
+		hwsio = malloc(sizeof(*hwsio));
+		if (!hwsio) {
+			err = -ENOMEM;
+			goto out;
+		}
+
+		err = ipstats_dissect_hw_s_info_one(tb[attr_id_l3], hwsio, "l3");
+		if (err)
+			goto out;
+
+		hwsi->infos[IPSTATS_HW_S_INFO_IDX_L3_STATS] = hwsio;
+		hwsio = NULL;
+	}
+
+	return 0;
+
+out:
+	free(hwsio);
+	return err;
+}
+
+static void ipstats_fini_hw_s_info(struct ipstats_hw_s_info *hwsi)
+{
+	int i;
+
+	for (i = 0; i < IPSTATS_HW_S_INFO_IDX_COUNT; i++)
+		free(hwsi->infos[i]);
+}
+
+static void
+__ipstats_show_hw_s_info_one(const struct ipstats_hw_s_info_one *hwsio)
+{
+	if (hwsio == NULL)
+		return;
+
+	ipstats_print_moo(PRINT_ANY, "request", " %s", hwsio->request);
+	ipstats_print_moo(PRINT_ANY, "used", " used %s", hwsio->used);
+}
+
+static void
+ipstats_show_hw_s_info_one(const struct ipstats_hw_s_info *hwsi,
+			   enum ipstats_hw_s_info_idx idx)
+{
+	const struct ipstats_hw_s_info_one *hwsio = hwsi->infos[idx];
+	const char *name = ipstats_hw_s_info_name[idx];
+
+	if (hwsio == NULL)
+		return;
+
+	print_string(PRINT_FP, NULL, "    %s", name);
+	open_json_object(name);
+	__ipstats_show_hw_s_info_one(hwsio);
+	close_json_object();
+}
+
+static int __ipstats_show_hw_s_info(const struct rtattr *at)
+{
+	struct ipstats_hw_s_info hwsi = {};
+	int err;
+
+	err = ipstats_dissect_hw_s_info(at, &hwsi);
+	if (err)
+		return err;
+
+	open_json_object("info");
+	ipstats_show_hw_s_info_one(&hwsi, IPSTATS_HW_S_INFO_IDX_L3_STATS);
+	close_json_object();
+
+	ipstats_fini_hw_s_info(&hwsi);
+	return 0;
+}
+
+static int ipstats_show_hw_s_info(struct ipstats_stat_show_attrs *attrs,
+				  unsigned int group, unsigned int subgroup)
+{
+	const struct rtattr *at;
+	int err;
+
+	at = ipstats_stat_show_get_attr(attrs, group, subgroup, &err);
+	if (at == NULL)
+		return err;
+
+	print_nl();
+	return __ipstats_show_hw_s_info(at);
+}
+
 static void
 ipstats_stat_desc_pack_cpu_hit(struct ipstats_stat_dump_filters *filters,
 			       const struct ipstats_stat_desc *desc)
@@ -189,8 +380,34 @@ static const struct ipstats_stat_desc ipstats_stat_desc_offload_cpu_hit = {
 	.show = &ipstats_stat_desc_show_cpu_hit,
 };
 
+static void
+ipstats_stat_desc_pack_hw_stats_info(struct ipstats_stat_dump_filters *filters,
+				     const struct ipstats_stat_desc *desc)
+{
+	ipstats_stat_desc_enable_bit(filters,
+				     IFLA_STATS_LINK_OFFLOAD_XSTATS,
+				     IFLA_OFFLOAD_XSTATS_HW_S_INFO);
+}
+
+static int
+ipstats_stat_desc_show_hw_stats_info(struct ipstats_stat_show_attrs *attrs,
+				     const struct ipstats_stat_desc *desc)
+{
+	return ipstats_show_hw_s_info(attrs,
+				      IFLA_STATS_LINK_OFFLOAD_XSTATS,
+				      IFLA_OFFLOAD_XSTATS_HW_S_INFO);
+}
+
+static const struct ipstats_stat_desc ipstats_stat_desc_offload_hw_s_info = {
+	.name = "hw_stats_info",
+	.kind = IPSTATS_STAT_DESC_KIND_LEAF,
+	.pack = &ipstats_stat_desc_pack_hw_stats_info,
+	.show = &ipstats_stat_desc_show_hw_stats_info,
+};
+
 static const struct ipstats_stat_desc *ipstats_stat_desc_offload_subs[] = {
 	&ipstats_stat_desc_offload_cpu_hit,
+	&ipstats_stat_desc_offload_hw_s_info,
 };
 
 static const struct ipstats_stat_desc ipstats_stat_desc_offload_group = {
-- 
2.31.1


^ permalink raw reply related

* [PATCH iproute2-next 09/11] ipstats: Add offload subgroup "l3_stats"
From: Petr Machata @ 2022-04-22  8:30 UTC (permalink / raw)
  To: netdev; +Cc: David Ahern, Stephen Hemminger, Ido Schimmel, Petr Machata
In-Reply-To: <cover.1650615982.git.petrm@nvidia.com>

Add into the group "offload" a subgroup "l3_stats" for showing
L3 statistics.

For example:

 # ip stats show dev swp2.200 group offload subgroup l3_stats
 4212: swp2.200: group offload subgroup l3_stats on used on
     RX: bytes packets errors dropped   mcast
          1920      21      1       0       0
     TX: bytes packets errors dropped
           756       9      0       0

 # ip -j stats show dev swp2.200 group offload subgroup l3_stats | jq
 [
   {
     "ifindex": 4212,
     "ifname": "swp2.200",
     "group": "offload",
     "subgroup": "l3_stats",
     "info": {
       "request": true,
       "used": true
     },
     "stats64": {
       "rx": {
         "bytes": 1920,
         "packets": 21,
         "errors": 1,
         "dropped": 0,
         "multicast": 0
       },
       "tx": {
         "bytes": 756,
         "packets": 9,
         "errors": 0,
         "dropped": 0
       }
     }
   }
 ]

Signed-off-by: Petr Machata <petrm@nvidia.com>
Reviewed-by: Ido Schimmel <idosch@nvidia.com>
---
 ip/ipstats.c | 158 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 158 insertions(+)

diff --git a/ip/ipstats.c b/ip/ipstats.c
index 4cdd5122..29ca0731 100644
--- a/ip/ipstats.c
+++ b/ip/ipstats.c
@@ -164,6 +164,82 @@ static int ipstats_show_64(struct ipstats_stat_show_attrs *attrs,
 	return 0;
 }
 
+static void print_hw_stats64(FILE *fp, struct rtnl_hw_stats64 *s)
+{
+	unsigned int cols[] = {
+		strlen("*X: bytes"),
+		strlen("packets"),
+		strlen("errors"),
+		strlen("dropped"),
+		strlen("overrun"),
+	};
+
+	if (is_json_context()) {
+		/* RX stats */
+		open_json_object("rx");
+		print_u64(PRINT_JSON, "bytes", NULL, s->rx_bytes);
+		print_u64(PRINT_JSON, "packets", NULL, s->rx_packets);
+		print_u64(PRINT_JSON, "errors", NULL, s->rx_errors);
+		print_u64(PRINT_JSON, "dropped", NULL, s->rx_dropped);
+		print_u64(PRINT_JSON, "multicast", NULL, s->multicast);
+		close_json_object();
+
+		/* TX stats */
+		open_json_object("tx");
+		print_u64(PRINT_JSON, "bytes", NULL, s->tx_bytes);
+		print_u64(PRINT_JSON, "packets", NULL, s->tx_packets);
+		print_u64(PRINT_JSON, "errors", NULL, s->tx_errors);
+		print_u64(PRINT_JSON, "dropped", NULL, s->tx_dropped);
+		close_json_object();
+	} else {
+		size_columns(cols, ARRAY_SIZE(cols),
+			     s->rx_bytes, s->rx_packets, s->rx_errors,
+			     s->rx_dropped, s->multicast);
+		size_columns(cols, ARRAY_SIZE(cols),
+			     s->tx_bytes, s->tx_packets, s->tx_errors,
+			     s->tx_dropped, 0);
+
+		/* RX stats */
+		fprintf(fp, "    RX: %*s %*s %*s %*s %*s%s",
+			cols[0] - 4, "bytes", cols[1], "packets",
+			cols[2], "errors", cols[3], "dropped",
+			cols[4], "mcast", _SL_);
+
+		fprintf(fp, "    ");
+		print_num(fp, cols[0], s->rx_bytes);
+		print_num(fp, cols[1], s->rx_packets);
+		print_num(fp, cols[2], s->rx_errors);
+		print_num(fp, cols[3], s->rx_dropped);
+		print_num(fp, cols[4], s->multicast);
+		fprintf(fp, "%s", _SL_);
+
+		/* TX stats */
+		fprintf(fp, "    TX: %*s %*s %*s %*s%s",
+			cols[0] - 4, "bytes", cols[1], "packets",
+			cols[2], "errors", cols[3], "dropped", _SL_);
+
+		fprintf(fp, "    ");
+		print_num(fp, cols[0], s->tx_bytes);
+		print_num(fp, cols[1], s->tx_packets);
+		print_num(fp, cols[2], s->tx_errors);
+		print_num(fp, cols[3], s->tx_dropped);
+	}
+}
+
+static int ipstats_show_hw64(const struct rtattr *at)
+{
+	struct rtnl_hw_stats64 *stats;
+
+	stats = IPSTATS_RTA_PAYLOAD(struct rtnl_hw_stats64, at);
+	if (stats == NULL) {
+		fprintf(stderr, "Error: attribute payload too short");
+		return -EINVAL;
+	}
+
+	print_hw_stats64(stdout, stats);
+	return 0;
+}
+
 enum ipstats_maybe_on_off {
 	IPSTATS_MOO_OFF = -1,
 	IPSTATS_MOO_INVALID,
@@ -355,6 +431,57 @@ static int ipstats_show_hw_s_info(struct ipstats_stat_show_attrs *attrs,
 	return __ipstats_show_hw_s_info(at);
 }
 
+static int __ipstats_show_hw_stats(const struct rtattr *at_hwsi,
+				   const struct rtattr *at_stats,
+				   enum ipstats_hw_s_info_idx idx)
+{
+	int err = 0;
+
+	if (at_hwsi != NULL) {
+		struct ipstats_hw_s_info hwsi = {};
+
+		err = ipstats_dissect_hw_s_info(at_hwsi, &hwsi);
+		if (err)
+			return err;
+
+		open_json_object("info");
+		__ipstats_show_hw_s_info_one(hwsi.infos[idx]);
+		close_json_object();
+
+		ipstats_fini_hw_s_info(&hwsi);
+	}
+
+	if (at_stats != NULL) {
+		print_nl();
+		open_json_object("stats64");
+		err = ipstats_show_hw64(at_stats);
+		close_json_object();
+	}
+
+	return err;
+}
+
+static int ipstats_show_hw_stats(struct ipstats_stat_show_attrs *attrs,
+				 unsigned int group,
+				 unsigned int hw_s_info,
+				 unsigned int hw_stats,
+				 enum ipstats_hw_s_info_idx idx)
+{
+	const struct rtattr *at_stats;
+	const struct rtattr *at_hwsi;
+	int err = 0;
+
+	at_hwsi = ipstats_stat_show_get_attr(attrs, group, hw_s_info, &err);
+	if (at_hwsi == NULL)
+		return err;
+
+	at_stats = ipstats_stat_show_get_attr(attrs, group, hw_stats, &err);
+	if (at_stats == NULL && err != 0)
+		return err;
+
+	return __ipstats_show_hw_stats(at_hwsi, at_stats, idx);
+}
+
 static void
 ipstats_stat_desc_pack_cpu_hit(struct ipstats_stat_dump_filters *filters,
 			       const struct ipstats_stat_desc *desc)
@@ -405,9 +532,40 @@ static const struct ipstats_stat_desc ipstats_stat_desc_offload_hw_s_info = {
 	.show = &ipstats_stat_desc_show_hw_stats_info,
 };
 
+static void
+ipstats_stat_desc_pack_l3_stats(struct ipstats_stat_dump_filters *filters,
+				const struct ipstats_stat_desc *desc)
+{
+	ipstats_stat_desc_enable_bit(filters,
+				     IFLA_STATS_LINK_OFFLOAD_XSTATS,
+				     IFLA_OFFLOAD_XSTATS_L3_STATS);
+	ipstats_stat_desc_enable_bit(filters,
+				     IFLA_STATS_LINK_OFFLOAD_XSTATS,
+				     IFLA_OFFLOAD_XSTATS_HW_S_INFO);
+}
+
+static int
+ipstats_stat_desc_show_l3_stats(struct ipstats_stat_show_attrs *attrs,
+				const struct ipstats_stat_desc *desc)
+{
+	return ipstats_show_hw_stats(attrs,
+				     IFLA_STATS_LINK_OFFLOAD_XSTATS,
+				     IFLA_OFFLOAD_XSTATS_HW_S_INFO,
+				     IFLA_OFFLOAD_XSTATS_L3_STATS,
+				     IPSTATS_HW_S_INFO_IDX_L3_STATS);
+}
+
+static const struct ipstats_stat_desc ipstats_stat_desc_offload_l3_stats = {
+	.name = "l3_stats",
+	.kind = IPSTATS_STAT_DESC_KIND_LEAF,
+	.pack = &ipstats_stat_desc_pack_l3_stats,
+	.show = &ipstats_stat_desc_show_l3_stats,
+};
+
 static const struct ipstats_stat_desc *ipstats_stat_desc_offload_subs[] = {
 	&ipstats_stat_desc_offload_cpu_hit,
 	&ipstats_stat_desc_offload_hw_s_info,
+	&ipstats_stat_desc_offload_l3_stats,
 };
 
 static const struct ipstats_stat_desc ipstats_stat_desc_offload_group = {
-- 
2.31.1


^ permalink raw reply related

* [PATCH iproute2-next 11/11] man: Add man pages for the "stats" functions
From: Petr Machata @ 2022-04-22  8:31 UTC (permalink / raw)
  To: netdev; +Cc: David Ahern, Stephen Hemminger, Ido Schimmel, Petr Machata
In-Reply-To: <cover.1650615982.git.petrm@nvidia.com>

Add a man page for the new "stats" command.
Also mention the new "stats" group in ip-monitor.

Signed-off-by: Petr Machata <petrm@nvidia.com>
Reviewed-by: Ido Schimmel <idosch@nvidia.com>
---
 man/man8/ip-monitor.8 |   2 +-
 man/man8/ip-stats.8   | 160 ++++++++++++++++++++++++++++++++++++++++++
 man/man8/ip.8         |   7 +-
 3 files changed, 167 insertions(+), 2 deletions(-)
 create mode 100644 man/man8/ip-stats.8

diff --git a/man/man8/ip-monitor.8 b/man/man8/ip-monitor.8
index f886d31b..ec033c69 100644
--- a/man/man8/ip-monitor.8
+++ b/man/man8/ip-monitor.8
@@ -55,7 +55,7 @@ command is the first in the command line and then the object list follows:
 is the list of object types that we want to monitor.
 It may contain
 .BR link ", " address ", " route ", " mroute ", " prefix ", "
-.BR neigh ", " netconf ", "  rule ", " nsid " and " nexthop "."
+.BR neigh ", " netconf ", "  rule ", " stats ", " nsid " and " nexthop "."
 If no
 .B file
 argument is given,
diff --git a/man/man8/ip-stats.8 b/man/man8/ip-stats.8
new file mode 100644
index 00000000..7eaaf122
--- /dev/null
+++ b/man/man8/ip-stats.8
@@ -0,0 +1,160 @@
+.TH IP\-STATS 8 "16 Mar 2022" "iproute2" "Linux"
+.SH NAME
+ip-stats \- manage and show interface statistics
+.SH SYNOPSIS
+.sp
+.ad l
+.in +8
+.ti -8
+.B ip
+.B stats
+.RI  " { " COMMAND " | "
+.BR help " }"
+.sp
+
+.ti -8
+.BR "ip stats show"
+.RB "[ " dev
+.IR DEV " ] "
+.RB "[ " group
+.IR GROUP " [ "
+.BI subgroup " SUBGROUP"
+.R " ] ... ] ..."
+
+.ti -8
+.BR "ip stats set"
+.BI dev " DEV"
+.BR l3_stats " { "
+.BR on " | " off " }"
+
+.SH DESCRIPTION
+
+.TP
+.B ip stats set
+is used for toggling whether a certain HW statistics suite is collected on
+a given netdevice. The following statistics suites are supported:
+
+.in 21
+
+.ti 14
+.B l3_stats
+L3 stats reflect traffic that takes place in a HW device on an object that
+corresponds to the given software netdevice.
+
+.TP
+.B ip stats show
+is used for showing stats on a given netdevice, or dumping statistics
+across all netdevices. By default, all stats are requested. It is possible
+to filter which stats are requested by using the
+.B group
+and
+.B subgroup
+keywords.
+
+It is possible to specify several groups, or several subgroups for one
+group. When no subgroups are given for a group, all the subgroups are
+requested.
+
+The following groups are recognized:
+.in 21
+
+.ti 14
+.B group link
+- Link statistics. The same suite that "ip -s link show" shows.
+
+.ti 14
+.B group offload
+- A group that contains a number of HW-oriented statistics. See below for
+individual subgroups within this group.
+
+.TQ
+.BR "group offload " subgroups:
+.in 21
+
+.ti 14
+.B subgroup cpu_hit
+- The
+.B cpu_hit
+statistics suite is useful on hardware netdevices. The
+.B link
+statistics on these devices reflect both the hardware- and
+software-datapath traffic. The
+.B cpu_hit
+statistics then only reflect software-datapath traffic.
+
+.ti 14
+.B subgroup hw_stats_info
+- This suite does not include traffic statistics, but rather communicates
+the state of other statistics. Through this subgroup, it is possible to
+discover whether a given statistic was enabled, and when it was, whether
+any device driver actually configured its device to collect these
+statistics. For example,
+.B l3_stats
+was enabled in the following case, but no driver has installed it:
+
+# ip stats show dev swp1 group offload subgroup hw_stats_info
+.br
+56: swp1: group offload subgroup hw_stats_info
+.br
+    l3_stats on used off
+
+After an L3 address is added to the netdevice, the counter will be
+installed:
+
+# ip addr add dev swp1 192.0.2.1/28
+.br
+# ip stats show dev swp1 group offload subgroup hw_stats_info
+.br
+56: swp1: group offload subgroup hw_stats_info
+.br
+    l3_stats on used on
+
+.ti 14
+.B subgroup l3_stats
+- These statistics reflect L3 traffic that takes place in HW on an object
+that corresponds to the netdevice. Note that this suite is disabled by
+default and needs to be first enabled through
+.B ip stats set\fR.
+
+For example:
+
+# ip stats show dev swp2.200 group offload subgroup l3_stats
+.br
+112: swp2.200: group offload subgroup l3_stats on used on
+.br
+    RX:  bytes packets errors dropped   mcast
+.br
+          8900      72      2       0       3
+.br
+    TX:  bytes packets errors dropped
+.br
+          7176      58      0       0
+
+Note how the l3_stats_info for the selected group is also part of the dump.
+
+.SH EXAMPLES
+.PP
+# ip stats set dev swp1 l3_stats on
+.RS
+Enables collection of L3 HW statistics on swp1.
+.RE
+
+.PP
+# ip stats show group offload
+.RS
+Shows all offload statistics on all netdevices.
+.RE
+
+.PP
+# ip stats show dev swp1 group link
+.RS
+Shows link statistics on the given netdevice.
+.RE
+
+.SH SEE ALSO
+.br
+.BR ip (8),
+.BR ip-link (8),
+
+.SH AUTHOR
+Manpage by Petr Machata.
diff --git a/man/man8/ip.8 b/man/man8/ip.8
index 2a4848b7..f6adbc77 100644
--- a/man/man8/ip.8
+++ b/man/man8/ip.8
@@ -22,7 +22,7 @@ ip \- show / manipulate routing, network devices, interfaces and tunnels
 .BR link " | " address " | " addrlabel " | " route " | " rule " | " neigh " | "\
  ntable " | " tunnel " | " tuntap " | " maddress " | "  mroute " | " mrule " | "\
  monitor " | " xfrm " | " netns " | "  l2tp " | "  tcp_metrics " | " token " | "\
- macsec " | " vrf " | " mptcp " | " ioam " }"
+ macsec " | " vrf " | " mptcp " | " ioam " | " stats " }"
 .sp
 
 .ti -8
@@ -302,6 +302,10 @@ readability.
 .B rule
 - rule in routing policy database.
 
+.TP
+.B stats
+- manage and show interface statistics.
+
 .TP
 .B tcp_metrics/tcpmetrics
 - manage TCP Metrics
@@ -419,6 +423,7 @@ was written by Alexey N. Kuznetsov and added in Linux 2.2.
 .BR ip-ntable (8),
 .BR ip-route (8),
 .BR ip-rule (8),
+.BR ip-stats (8)
 .BR ip-tcp_metrics (8),
 .BR ip-token (8),
 .BR ip-tunnel (8),
-- 
2.31.1


^ permalink raw reply related

* [PATCH] NFC: nfcmrvl: fix error check return value of irq_of_parse_and_map()
From: cgel.zte @ 2022-04-22  8:46 UTC (permalink / raw)
  To: krzysztof.kozlowski, davem
  Cc: kuba, lv.ruyi, yashsri421, sameo, cuissard, netdev, linux-kernel,
	Zeal Robot

From: Lv Ruyi <lv.ruyi@zte.com.cn>

The irq_of_parse_and_map() function returns 0 on failure, and does not
return an negative value.

Fixes: 	b5b3e23e4cac ("NFC: nfcmrvl: add i2c driver")
Reported-by: Zeal Robot <zealci@zte.com.cn>
Signed-off-by: Lv Ruyi <lv.ruyi@zte.com.cn>
---
 drivers/nfc/nfcmrvl/i2c.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/nfc/nfcmrvl/i2c.c b/drivers/nfc/nfcmrvl/i2c.c
index ceef81d93ac9..7dcc97707363 100644
--- a/drivers/nfc/nfcmrvl/i2c.c
+++ b/drivers/nfc/nfcmrvl/i2c.c
@@ -167,7 +167,7 @@ static int nfcmrvl_i2c_parse_dt(struct device_node *node,
 		pdata->irq_polarity = IRQF_TRIGGER_RISING;
 
 	ret = irq_of_parse_and_map(node, 0);
-	if (ret < 0) {
+	if (!ret) {
 		pr_err("Unable to get irq, error: %d\n", ret);
 		return ret;
 	}
-- 
2.25.1



^ permalink raw reply related

* Re: [PATCH net-next] net: generalize skb freeing deferral to per-cpu lists
From: Paolo Abeni @ 2022-04-22  9:02 UTC (permalink / raw)
  To: Eric Dumazet, David S . Miller, Jakub Kicinski; +Cc: netdev, Eric Dumazet
In-Reply-To: <20220421153920.3637792-1-eric.dumazet@gmail.com>

Hi,

Looks great! I have a few questions below mostly to understand better
how it works...

On Thu, 2022-04-21 at 08:39 -0700, Eric Dumazet wrote:
> diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
> index 84d78df60453955a8eaf05847f6e2145176a727a..2fe311447fae5e860eee95f6e8772926d4915e9f 100644
> --- a/include/linux/skbuff.h
> +++ b/include/linux/skbuff.h
> @@ -1080,6 +1080,7 @@ struct sk_buff {
>  		unsigned int	sender_cpu;
>  	};
>  #endif
> +	u16			alloc_cpu;

I *think* we could in theory fetch the CPU that allocated the skb from
the napi_id - adding a cpu field to napi_struct and implementing an
helper to fetch it. Have you considered that option? or the napi lookup
would be just too expensive?

[...]

> diff --git a/net/core/dev.c b/net/core/dev.c
> index 4a77ebda4fb155581a5f761a864446a046987f51..4136d9c0ada6870ea0f7689702bdb5f0bbf29145 100644
> --- a/net/core/dev.c
> +++ b/net/core/dev.c
> @@ -4545,6 +4545,12 @@ static void rps_trigger_softirq(void *data)
>  
>  #endif /* CONFIG_RPS */
>  
> +/* Called from hardirq (IPI) context */
> +static void trigger_rx_softirq(void *data)

Perhaps '__always_unused' ? (But the compiler doesn't complain here)

> @@ -6486,3 +6487,46 @@ void __skb_ext_put(struct skb_ext *ext)
>  }
>  EXPORT_SYMBOL(__skb_ext_put);
>  #endif /* CONFIG_SKB_EXTENSIONS */
> +
> +/**
> + * skb_attempt_defer_free - queue skb for remote freeing
> + * @skb: buffer
> + *
> + * Put @skb in a per-cpu list, using the cpu which
> + * allocated the skb/pages to reduce false sharing
> + * and memory zone spinlock contention.
> + */
> +void skb_attempt_defer_free(struct sk_buff *skb)
> +{
> +	int cpu = skb->alloc_cpu;
> +	struct softnet_data *sd;
> +	unsigned long flags;
> +	bool kick;
> +
> +	if (WARN_ON_ONCE(cpu >= nr_cpu_ids) || !cpu_online(cpu)) {
> +		__kfree_skb(skb);
> +		return;
> +	}

I'm wondering if we should skip even when cpu == smp_processor_id()? 

> +
> +	sd = &per_cpu(softnet_data, cpu);
> +	/* We do not send an IPI or any signal.
> +	 * Remote cpu will eventually call skb_defer_free_flush()
> +	 */
> +	spin_lock_irqsave(&sd->skb_defer_list.lock, flags);
> +	__skb_queue_tail(&sd->skb_defer_list, skb);
> +
> +	/* kick every time queue length reaches 128.
> +	 * This should avoid blocking in smp_call_function_single_async().
> +	 * This condition should hardly be bit under normal conditions,
> +	 * unless cpu suddenly stopped to receive NIC interrupts.
> +	 */
> +	kick = skb_queue_len(&sd->skb_defer_list) == 128;

Out of sheer curiosity why 128? I guess it's should be larger then
NAPI_POLL_WEIGHT, to cope with with maximum theorethical burst len?

Thanks!

Paolo


^ permalink raw reply

* [PATCH v2 0/2] mwifiex: Select firmware based on strapping
From: Andrejs Cainikovs @ 2022-04-22  9:03 UTC (permalink / raw)
  To: linux-wireless, Amitkumar Karwar, Ganapathi Bhat,
	Sharvari Harisangam, Xinming Hu
  Cc: Andrejs Cainikovs, Kalle Valo, David S . Miller, Jakub Kicinski,
	Brian Norris, Jonas Dreßler, Alvin Šipraga,
	Francesco Dolcini, netdev

This patch set introduces a way to automatically select appropriate
firmware depending of the host connection method.

Andrejs Cainikovs (2):
  mwifiex: Select firmware based on strapping
  mwifiex: Add SD8997 SDIO-UART firmware

 drivers/net/wireless/marvell/mwifiex/sdio.c | 23 ++++++++++++++++++++-
 drivers/net/wireless/marvell/mwifiex/sdio.h |  6 ++++++
 2 files changed, 28 insertions(+), 1 deletion(-)

-- 
2.25.1


^ permalink raw reply

* [PATCH v2 1/2] mwifiex: Select firmware based on strapping
From: Andrejs Cainikovs @ 2022-04-22  9:03 UTC (permalink / raw)
  To: linux-wireless, Amitkumar Karwar, Ganapathi Bhat,
	Sharvari Harisangam, Xinming Hu
  Cc: Andrejs Cainikovs, Kalle Valo, David S . Miller, Jakub Kicinski,
	Brian Norris, Jonas Dreßler, Alvin Šipraga,
	Francesco Dolcini, netdev
In-Reply-To: <20220422090313.125857-1-andrejs.cainikovs@toradex.com>

Some WiFi/Bluetooth modules might have different host connection
options, allowing to either use SDIO for both WiFi and Bluetooth,
or SDIO for WiFi and UART for Bluetooth. It is possible to detect
whether a module has SDIO-SDIO or SDIO-UART connection by reading
its host strap register.

This change introduces a way to automatically select appropriate
firmware depending of the connection method, and removes a need
of symlinking or overwriting the original firmware file with a
required one.

Host strap register used in this commit comes from the NXP driver [1]
hosted at Code Aurora.

[1] https://source.codeaurora.org/external/imx/linux-imx/tree/drivers/net/wireless/nxp/mxm_wifiex/wlan_src/mlinux/moal_sdio_mmc.c?h=rel_imx_5.4.70_2.3.2&id=688b67b2c7220b01521ffe560da7eee33042c7bd#n1274

Signed-off-by: Andrejs Cainikovs <andrejs.cainikovs@toradex.com>
Reviewed-by: Alvin Šipraga <alsi@bang-olufsen.dk>
---
 drivers/net/wireless/marvell/mwifiex/sdio.c | 21 ++++++++++++++++++++-
 drivers/net/wireless/marvell/mwifiex/sdio.h |  5 +++++
 2 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c
index 4f3238d2a171..d26efd56be08 100644
--- a/drivers/net/wireless/marvell/mwifiex/sdio.c
+++ b/drivers/net/wireless/marvell/mwifiex/sdio.c
@@ -182,6 +182,9 @@ static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8997 = {
 	.host_int_rsr_reg = 0x4,
 	.host_int_status_reg = 0x0C,
 	.host_int_mask_reg = 0x08,
+	.host_strap_reg = 0xF4,
+	.host_strap_mask = 0x01,
+	.host_strap_value = 0x00,
 	.status_reg_0 = 0xE8,
 	.status_reg_1 = 0xE9,
 	.sdio_int_mask = 0xff,
@@ -283,6 +286,9 @@ static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8987 = {
 	.host_int_rsr_reg = 0x4,
 	.host_int_status_reg = 0x0C,
 	.host_int_mask_reg = 0x08,
+	.host_strap_reg = 0xF4,
+	.host_strap_mask = 0x01,
+	.host_strap_value = 0x00,
 	.status_reg_0 = 0xE8,
 	.status_reg_1 = 0xE9,
 	.sdio_int_mask = 0xff,
@@ -536,6 +542,7 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id)
 		struct mwifiex_sdio_device *data = (void *)id->driver_data;
 
 		card->firmware = data->firmware;
+		card->firmware_sdiouart = data->firmware_sdiouart;
 		card->reg = data->reg;
 		card->max_ports = data->max_ports;
 		card->mp_agg_pkt_limit = data->mp_agg_pkt_limit;
@@ -2439,6 +2446,7 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
 	int ret;
 	struct sdio_mmc_card *card = adapter->card;
 	struct sdio_func *func = card->func;
+	const char *firmware = card->firmware;
 
 	/* save adapter pointer in card */
 	card->adapter = adapter;
@@ -2455,7 +2463,18 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
 		return ret;
 	}
 
-	strcpy(adapter->fw_name, card->firmware);
+	/* Select correct firmware (sdsd or sdiouart) firmware based on the strapping
+	 * option
+	 */
+	if (card->firmware_sdiouart) {
+		u8 val;
+
+		mwifiex_read_reg(adapter, card->reg->host_strap_reg, &val);
+		if ((val & card->reg->host_strap_mask) == card->reg->host_strap_value)
+			firmware = card->firmware_sdiouart;
+	}
+	strcpy(adapter->fw_name, firmware);
+
 	if (card->fw_dump_enh) {
 		adapter->mem_type_mapping_tbl = generic_mem_type_map;
 		adapter->num_mem_types = 1;
diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.h b/drivers/net/wireless/marvell/mwifiex/sdio.h
index 5648512c9300..ad2c28cbb630 100644
--- a/drivers/net/wireless/marvell/mwifiex/sdio.h
+++ b/drivers/net/wireless/marvell/mwifiex/sdio.h
@@ -196,6 +196,9 @@ struct mwifiex_sdio_card_reg {
 	u8 host_int_rsr_reg;
 	u8 host_int_status_reg;
 	u8 host_int_mask_reg;
+	u8 host_strap_reg;
+	u8 host_strap_mask;
+	u8 host_strap_value;
 	u8 status_reg_0;
 	u8 status_reg_1;
 	u8 sdio_int_mask;
@@ -241,6 +244,7 @@ struct sdio_mmc_card {
 
 	struct completion fw_done;
 	const char *firmware;
+	const char *firmware_sdiouart;
 	const struct mwifiex_sdio_card_reg *reg;
 	u8 max_ports;
 	u8 mp_agg_pkt_limit;
@@ -274,6 +278,7 @@ struct sdio_mmc_card {
 
 struct mwifiex_sdio_device {
 	const char *firmware;
+	const char *firmware_sdiouart;
 	const struct mwifiex_sdio_card_reg *reg;
 	u8 max_ports;
 	u8 mp_agg_pkt_limit;
-- 
2.25.1


^ permalink raw reply related

* [PATCH v2 2/2] mwifiex: Add SD8997 SDIO-UART firmware
From: Andrejs Cainikovs @ 2022-04-22  9:03 UTC (permalink / raw)
  To: linux-wireless, Amitkumar Karwar, Ganapathi Bhat,
	Sharvari Harisangam, Xinming Hu
  Cc: Andrejs Cainikovs, Kalle Valo, David S . Miller, Jakub Kicinski,
	Brian Norris, Jonas Dreßler, Alvin Šipraga,
	Francesco Dolcini, netdev
In-Reply-To: <20220422090313.125857-1-andrejs.cainikovs@toradex.com>

With a recent change now it is possible to detect the strapping
option on SD8997, which allows to pick up a correct firmware
for either SDIO-SDIO or SDIO-UART.

This commit enables SDIO-UART firmware on SD8997.

Signed-off-by: Andrejs Cainikovs <andrejs.cainikovs@toradex.com>
---
 drivers/net/wireless/marvell/mwifiex/sdio.c | 2 ++
 drivers/net/wireless/marvell/mwifiex/sdio.h | 1 +
 2 files changed, 3 insertions(+)

diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c
index d26efd56be08..76004bda0c02 100644
--- a/drivers/net/wireless/marvell/mwifiex/sdio.c
+++ b/drivers/net/wireless/marvell/mwifiex/sdio.c
@@ -408,6 +408,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8977 = {
 
 static const struct mwifiex_sdio_device mwifiex_sdio_sd8997 = {
 	.firmware = SD8997_DEFAULT_FW_NAME,
+	.firmware_sdiouart = SD8997_SDIOUART_FW_NAME,
 	.reg = &mwifiex_reg_sd8997,
 	.max_ports = 32,
 	.mp_agg_pkt_limit = 16,
@@ -3176,3 +3177,4 @@ MODULE_FIRMWARE(SD8887_DEFAULT_FW_NAME);
 MODULE_FIRMWARE(SD8977_DEFAULT_FW_NAME);
 MODULE_FIRMWARE(SD8987_DEFAULT_FW_NAME);
 MODULE_FIRMWARE(SD8997_DEFAULT_FW_NAME);
+MODULE_FIRMWARE(SD8997_SDIOUART_FW_NAME);
diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.h b/drivers/net/wireless/marvell/mwifiex/sdio.h
index ad2c28cbb630..28e8f76bdd58 100644
--- a/drivers/net/wireless/marvell/mwifiex/sdio.h
+++ b/drivers/net/wireless/marvell/mwifiex/sdio.h
@@ -39,6 +39,7 @@
 #define SD8977_DEFAULT_FW_NAME "mrvl/sdsd8977_combo_v2.bin"
 #define SD8987_DEFAULT_FW_NAME "mrvl/sd8987_uapsta.bin"
 #define SD8997_DEFAULT_FW_NAME "mrvl/sdsd8997_combo_v4.bin"
+#define SD8997_SDIOUART_FW_NAME "mrvl/sdiouart8997_combo_v4.bin"
 
 #define BLOCK_MODE	1
 #define BYTE_MODE	0
-- 
2.25.1


^ permalink raw reply related

* Re: [PATCH net] net/smc: sync err code when tcp connection was refused
From: Karsten Graul @ 2022-04-22  9:24 UTC (permalink / raw)
  To: liuyacan
  Cc: davem, kuba, linux-kernel, linux-s390, netdev, pabeni, tonylu,
	ubraun
In-Reply-To: <20220421115805.1642771-1-liuyacan@corp.netease.com>

On 21/04/2022 13:58, liuyacan@corp.netease.com wrote:
> From: Yacan Liu <liuyacan@corp.netease.com>
> 
> Forgot to cc ubraun@linux.ibm.com

No problem, its okay to add the current maintainers. 
Ursula left the company in the meantime.

^ permalink raw reply

* Re: [RFC PATCH] Bluetooth: core: Allow bind HCI socket user channel when HCI is UP.
From: Marcel Holtmann @ 2022-04-22  9:20 UTC (permalink / raw)
  To: Vasyl Vavrychuk
  Cc: LKML, open list:NETWORKING [GENERAL], BlueZ, Johan Hedberg,
	Luiz Augusto von Dentz, David S. Miller, Jakub Kicinski,
	Paolo Abeni, Vasyl Vavrychuk
In-Reply-To: <20220412120945.28862-1-vasyl.vavrychuk@opensynergy.com>

Hi Vasyl,

> This is needed for user-space to ensure that HCI init scheduled from
> hci_register_dev is completed.
> 
> Function hci_register_dev queues power_on workqueue which will run
> hci_power_on > hci_dev_do_open. Function hci_dev_do_open sets HCI_INIT
> for some time.
> 
> It is not allowed to bind to HCI socket user channel when HCI_INIT is
> set. As result, bind might fail when user-space program is run early
> enough during boot.
> 
> Now, user-space program can first issue HCIDEVUP ioctl to ensure HCI
> init scheduled at hci_register_dev was completed.
> 
> Signed-off-by: Vasyl Vavrychuk <vasyl.vavrychuk@opensynergy.com>
> ---
> net/bluetooth/hci_sock.c | 4 +---
> 1 file changed, 1 insertion(+), 3 deletions(-)
> 
> diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
> index 33b3c0ffc339..c98de809f856 100644
> --- a/net/bluetooth/hci_sock.c
> +++ b/net/bluetooth/hci_sock.c
> @@ -1194,9 +1194,7 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
> 
> 		if (test_bit(HCI_INIT, &hdev->flags) ||
> 		    hci_dev_test_flag(hdev, HCI_SETUP) ||
> -		    hci_dev_test_flag(hdev, HCI_CONFIG) ||
> -		    (!hci_dev_test_flag(hdev, HCI_AUTO_OFF) &&
> -		     test_bit(HCI_UP, &hdev->flags))) {
> +		    hci_dev_test_flag(hdev, HCI_CONFIG)) {
> 			err = -EBUSY;
> 			hci_dev_put(hdev);
> 			goto done;

I am not following the reasoning here. It is true that the device has to run init before you can do something with it. From mgmt interface your device will only be announced when it is really ready.

Regards

Marcel


^ permalink raw reply

* Re: [PATCH] net: cdc-ncm:  Move spin_lock_bh() to spin_lock()
From: patchwork-bot+netdevbpf @ 2022-04-22  9:30 UTC (permalink / raw)
  To: Yunbo Yu; +Cc: oliver, davem, kuba, pabeni, linux-usb, netdev, linux-kernel
In-Reply-To: <20220418141812.1241307-1-yuyunbo519@gmail.com>

Hello:

This patch was applied to netdev/net-next.git (master)
by David S. Miller <davem@davemloft.net>:

On Mon, 18 Apr 2022 22:18:12 +0800 you wrote:
> It is unnecessary to call spin_lock_bh() for you are already in a tasklet.
> 
> Signed-off-by: Yunbo Yu <yuyunbo519@gmail.com>
> ---
>  drivers/net/usb/cdc_ncm.c | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)

Here is the summary with links:
  - net: cdc-ncm: Move spin_lock_bh() to spin_lock()
    https://git.kernel.org/netdev/net-next/c/f1ed409fb1ee

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* Re: [PATCH] myri10ge: remove redundant assignment to variable status
From: patchwork-bot+netdevbpf @ 2022-04-22  9:30 UTC (permalink / raw)
  To: Colin Ian King
  Cc: christopher.lee, davem, kuba, pabeni, netdev, kernel-janitors,
	linux-kernel, llvm
In-Reply-To: <20220418143759.85626-1-colin.i.king@gmail.com>

Hello:

This patch was applied to netdev/net-next.git (master)
by David S. Miller <davem@davemloft.net>:

On Mon, 18 Apr 2022 15:37:59 +0100 you wrote:
> Variable status is being assigned a value that is never read, it is
> being re-assigned again later on. The assignment is redundant and can
> be removed.
> 
> Cleans up clang scan build warning:
> drivers/net/ethernet/myricom/myri10ge/myri10ge.c:582:7: warning: Although
> the value stored to 'status' is used in the enclosing expression, the
> value is never actually read from 'status' [deadcode.DeadStores]
> 
> [...]

Here is the summary with links:
  - myri10ge: remove redundant assignment to variable status
    https://git.kernel.org/netdev/net-next/c/1c604f91b773

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* Re: [PATCH] net: cosa: fix error check return value of register_chrdev()
From: patchwork-bot+netdevbpf @ 2022-04-22  9:30 UTC (permalink / raw)
  To: Lv Ruyi; +Cc: kas, davem, kuba, pabeni, netdev, linux-kernel, lv.ruyi, zealci
In-Reply-To: <20220418105834.2558892-1-lv.ruyi@zte.com.cn>

Hello:

This patch was applied to netdev/net.git (master)
by David S. Miller <davem@davemloft.net>:

On Mon, 18 Apr 2022 10:58:34 +0000 you wrote:
> From: Lv Ruyi <lv.ruyi@zte.com.cn>
> 
> If major equal 0, register_chrdev() returns error code when it fails.
> This function dynamically allocate a major and return its number on
> success, so we should use "< 0" to check it instead of "!".
> 
> Reported-by: Zeal Robot <zealci@zte.com.cn>
> Signed-off-by: Lv Ruyi <lv.ruyi@zte.com.cn>
> 
> [...]

Here is the summary with links:
  - net: cosa: fix error check return value of register_chrdev()
    https://git.kernel.org/netdev/net/c/d48fea8401cf

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* Re: [PATCH net-next] net/ipv6: Enforce limits for accept_unsolicited_na sysctl
From: patchwork-bot+netdevbpf @ 2022-04-22  9:40 UTC (permalink / raw)
  To: Arun Ajith S; +Cc: netdev, davem, linux-kernel, dsahern, yoshfuji, kuba, pabeni
In-Reply-To: <20220419105910.686-1-aajith@arista.com>

Hello:

This patch was applied to netdev/net-next.git (master)
by David S. Miller <davem@davemloft.net>:

On Tue, 19 Apr 2022 10:59:10 +0000 you wrote:
> Fix mistake in the original patch where limits were specified but the
> handler didn't take care of the limits.
> 
> Signed-off-by: Arun Ajith S <aajith@arista.com>
> ---
>  net/ipv6/addrconf.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)

Here is the summary with links:
  - [net-next] net/ipv6: Enforce limits for accept_unsolicited_na sysctl
    https://git.kernel.org/netdev/net-next/c/d09d3ec03f02

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* Re: [PATCH] net: ethernet: ti: am65-cpsw-ethtool: use pm_runtime_resume_and_get
From: patchwork-bot+netdevbpf @ 2022-04-22 10:00 UTC (permalink / raw)
  To: Lv Ruyi; +Cc: davem, kuba, netdev, linux-kernel, chi.minghao, zealci
In-Reply-To: <20220419110352.2574359-1-chi.minghao@zte.com.cn>

Hello:

This patch was applied to netdev/net-next.git (master)
by David S. Miller <davem@davemloft.net>:

On Tue, 19 Apr 2022 11:03:52 +0000 you wrote:
> From: Minghao Chi <chi.minghao@zte.com.cn>
> 
> Using pm_runtime_resume_and_get() to replace pm_runtime_get_sync and
> pm_runtime_put_noidle. This change is just to simplify the code, no
> actual functional changes.
> 
> Reported-by: Zeal Robot <zealci@zte.com.cn>
> Signed-off-by: Minghao Chi <chi.minghao@zte.com.cn>
> 
> [...]

Here is the summary with links:
  - net: ethernet: ti: am65-cpsw-ethtool: use pm_runtime_resume_and_get
    https://git.kernel.org/netdev/net-next/c/e350dbac3c09

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* [PATCH perf/core 0/5] perf tools: Fix prologue generation
From: Jiri Olsa @ 2022-04-22 10:00 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko
  Cc: linux-perf-users, netdev, bpf, Ingo Molnar, Namhyung Kim,
	Alexander Shishkin, Peter Zijlstra, Martin KaFai Lau, Song Liu,
	Yonghong Song, John Fastabend, Ian Rogers

hi,
sending change we discussed some time ago [1] to get rid of
some deprecated functions we use in perf prologue code.

Despite the gloomy discussion I think the final code does
not look that bad ;-)

This patchset removes following libbpf functions from perf:
  bpf_program__set_prep
  bpf_program__nth_fd
  struct bpf_prog_prep_result

Also available in:
  git://git.kernel.org/pub/scm/linux/kernel/git/jolsa/perf.git
  perf/depre

thanks,
jirka


[1] https://lore.kernel.org/bpf/CAEf4BzaiBO3_617kkXZdYJ8hS8YF--ZLgapNbgeeEJ-pY0H88g@mail.gmail.com/
---
Jiri Olsa (5):
      libbpf: Add bpf_program__set_insns function
      libbpf: Load prog's instructions after prog_prepare_load_fn callback
      perf tools: Move libbpf init in libbpf_init function
      perf tools: Register perfkprobe libbpf section handler
      perf tools: Rework prologue generation code

 tools/lib/bpf/libbpf.c                      |  10 +++++
 tools/lib/bpf/libbpf.h                      |  12 ++++++
 tools/lib/bpf/libbpf.map                    |   3 +-
 tools/perf/include/bpf/bpf.h                |   2 +-
 tools/perf/tests/bpf-script-example.c       |   2 +-
 tools/perf/tests/bpf-script-test-prologue.c |   2 +-
 tools/perf/util/bpf-loader.c                | 213 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
 7 files changed, 212 insertions(+), 32 deletions(-)

^ permalink raw reply

* [PATCH perf/core 1/5] libbpf: Add bpf_program__set_insns function
From: Jiri Olsa @ 2022-04-22 10:00 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko
  Cc: linux-perf-users, netdev, bpf, Ingo Molnar, Namhyung Kim,
	Alexander Shishkin, Peter Zijlstra, Martin KaFai Lau, Song Liu,
	Yonghong Song, John Fastabend, Ian Rogers
In-Reply-To: <20220422100025.1469207-1-jolsa@kernel.org>

Adding bpf_program__set_insns that allows to set new
instructions for program.

Also moving bpf_program__attach_kprobe_multi_opts on
the proper name sorted place in map file.

Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
 tools/lib/bpf/libbpf.c   |  8 ++++++++
 tools/lib/bpf/libbpf.h   | 12 ++++++++++++
 tools/lib/bpf/libbpf.map |  3 ++-
 3 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 809fe209cdcc..284790d81c1b 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -8457,6 +8457,14 @@ size_t bpf_program__insn_cnt(const struct bpf_program *prog)
 	return prog->insns_cnt;
 }
 
+void bpf_program__set_insns(struct bpf_program *prog,
+			    struct bpf_insn *insns, size_t insns_cnt)
+{
+	free(prog->insns);
+	prog->insns = insns;
+	prog->insns_cnt = insns_cnt;
+}
+
 int bpf_program__set_prep(struct bpf_program *prog, int nr_instances,
 			  bpf_program_prep_t prep)
 {
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 05dde85e19a6..b31ad58d335f 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -323,6 +323,18 @@ struct bpf_insn;
  * different.
  */
 LIBBPF_API const struct bpf_insn *bpf_program__insns(const struct bpf_program *prog);
+
+/**
+ * @brief **bpf_program__set_insns()** can set BPF program's underlying
+ * BPF instructions.
+ * @param prog BPF program for which to return instructions
+ * @param insn a pointer to an array of BPF instructions
+ * @param insns_cnt number of `struct bpf_insn`'s that form
+ * specified BPF program
+ */
+LIBBPF_API void bpf_program__set_insns(struct bpf_program *prog,
+				       struct bpf_insn *insns, size_t insns_cnt);
+
 /**
  * @brief **bpf_program__insn_cnt()** returns number of `struct bpf_insn`'s
  * that form specified BPF program.
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index dd35ee58bfaa..afa10d24ab41 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -444,7 +444,8 @@ LIBBPF_0.8.0 {
 	global:
 		bpf_object__destroy_subskeleton;
 		bpf_object__open_subskeleton;
+		bpf_program__attach_kprobe_multi_opts;
+		bpf_program__set_insns;
 		libbpf_register_prog_handler;
 		libbpf_unregister_prog_handler;
-		bpf_program__attach_kprobe_multi_opts;
 } LIBBPF_0.7.0;
-- 
2.35.1


^ permalink raw reply related

* [PATCH perf/core 2/5] libbpf: Load prog's instructions after prog_prepare_load_fn callback
From: Jiri Olsa @ 2022-04-22 10:00 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko
  Cc: linux-perf-users, netdev, bpf, Ingo Molnar, Namhyung Kim,
	Alexander Shishkin, Peter Zijlstra, Martin KaFai Lau, Song Liu,
	Yonghong Song, John Fastabend, Ian Rogers
In-Reply-To: <20220422100025.1469207-1-jolsa@kernel.org>

We can have changed instructions after calling
prog_prepare_load_fn callback, reload them.

Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
 tools/lib/bpf/libbpf.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 284790d81c1b..c8df74e5f658 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -6695,6 +6695,8 @@ static int bpf_object_load_prog_instance(struct bpf_object *obj, struct bpf_prog
 				prog->name, err);
 			return err;
 		}
+		insns = prog->insns;
+		insns_cnt = prog->insns_cnt;
 	}
 
 	if (obj->gen_loader) {
-- 
2.35.1


^ permalink raw reply related

* [PATCH perf/core 3/5] perf tools: Move libbpf init in libbpf_init function
From: Jiri Olsa @ 2022-04-22 10:00 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko
  Cc: linux-perf-users, netdev, bpf, Ingo Molnar, Namhyung Kim,
	Alexander Shishkin, Peter Zijlstra, Martin KaFai Lau, Song Liu,
	Yonghong Song, John Fastabend, Ian Rogers
In-Reply-To: <20220422100025.1469207-1-jolsa@kernel.org>

Moving the libbpf init code into single function,
so we have single place doing that.

Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
 tools/perf/util/bpf-loader.c | 27 ++++++++++++++++++---------
 1 file changed, 18 insertions(+), 9 deletions(-)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index b72cef1ae959..f8ad581ea247 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -99,16 +99,26 @@ static int bpf_perf_object__add(struct bpf_object *obj)
 	return perf_obj ? 0 : -ENOMEM;
 }
 
+static int libbpf_init(void)
+{
+	if (libbpf_initialized)
+		return 0;
+
+	libbpf_set_print(libbpf_perf_print);
+	libbpf_initialized = true;
+	return 0;
+}
+
 struct bpf_object *
 bpf__prepare_load_buffer(void *obj_buf, size_t obj_buf_sz, const char *name)
 {
 	LIBBPF_OPTS(bpf_object_open_opts, opts, .object_name = name);
 	struct bpf_object *obj;
+	int err;
 
-	if (!libbpf_initialized) {
-		libbpf_set_print(libbpf_perf_print);
-		libbpf_initialized = true;
-	}
+	err = libbpf_init();
+	if (err)
+		return ERR_PTR(err);
 
 	obj = bpf_object__open_mem(obj_buf, obj_buf_sz, &opts);
 	if (IS_ERR_OR_NULL(obj)) {
@@ -135,14 +145,13 @@ struct bpf_object *bpf__prepare_load(const char *filename, bool source)
 {
 	LIBBPF_OPTS(bpf_object_open_opts, opts, .object_name = filename);
 	struct bpf_object *obj;
+	int err;
 
-	if (!libbpf_initialized) {
-		libbpf_set_print(libbpf_perf_print);
-		libbpf_initialized = true;
-	}
+	err = libbpf_init();
+	if (err)
+		return ERR_PTR(err);
 
 	if (source) {
-		int err;
 		void *obj_buf;
 		size_t obj_buf_sz;
 
-- 
2.35.1


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox