mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] selftests/acct: share netlink helpers
@ 2026-07-12 17:13 Yiyang Chen
  2026-07-13  2:34 ` Balbir Singh
  0 siblings, 1 reply; 2+ messages in thread
From: Yiyang Chen @ 2026-07-12 17:13 UTC (permalink / raw)
  To: Balbir Singh, Andrew Morton; +Cc: linux-kernel, Yiyang Chen

Extract the duplicated generic netlink boilerplate (netlink_open,
send_request, get_family_id, and NLA walker macros) from
cgroupstats.c and taskstats_fill_stats_tgid.c into a shared
netlink_helper.{h,c}.

Suggested-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Yiyang Chen <cyyzero16@gmail.com>
---
 tools/testing/selftests/acct/.gitignore       |   1 +
 tools/testing/selftests/acct/Makefile         |  10 ++
 tools/testing/selftests/acct/cgroupstats.c    | 133 +----------------
 tools/testing/selftests/acct/netlink_helper.c | 116 +++++++++++++++
 tools/testing/selftests/acct/netlink_helper.h |  44 ++++++
 .../acct/taskstats_fill_stats_tgid.c          | 134 ++----------------
 6 files changed, 184 insertions(+), 254 deletions(-)
 create mode 100644 tools/testing/selftests/acct/netlink_helper.c
 create mode 100644 tools/testing/selftests/acct/netlink_helper.h

diff --git a/tools/testing/selftests/acct/.gitignore b/tools/testing/selftests/acct/.gitignore
index 9e9c61c5bfd6..fe0896f54e15 100644
--- a/tools/testing/selftests/acct/.gitignore
+++ b/tools/testing/selftests/acct/.gitignore
@@ -1,4 +1,5 @@
 acct_syscall
 taskstats_fill_stats_tgid
+cgroupstats
 config
 process_log
diff --git a/tools/testing/selftests/acct/Makefile b/tools/testing/selftests/acct/Makefile
index db88d65f5581..93a11a28a636 100644
--- a/tools/testing/selftests/acct/Makefile
+++ b/tools/testing/selftests/acct/Makefile
@@ -3,7 +3,17 @@ TEST_GEN_PROGS := acct_syscall
 TEST_GEN_PROGS += taskstats_fill_stats_tgid
 TEST_GEN_PROGS += cgroupstats
 
+NETLINK_HELPER_PROGS := cgroupstats taskstats_fill_stats_tgid
+
 CFLAGS += -Wall
 LDLIBS += -lpthread
 
 include ../lib.mk
+
+$(NETLINK_HELPER_PROGS): %: %.c netlink_helper.c netlink_helper.h
+	$(call msg,CC,,$@)
+	$(Q)$(LINK.c) $< netlink_helper.c $(LDLIBS) -o $@
+
+$(addprefix $(OUTPUT)/,$(NETLINK_HELPER_PROGS)): $(OUTPUT)/%: %.c netlink_helper.c netlink_helper.h
+	$(call msg,CC,,$@)
+	$(Q)$(LINK.c) $< netlink_helper.c $(LDLIBS) -o $@
diff --git a/tools/testing/selftests/acct/cgroupstats.c b/tools/testing/selftests/acct/cgroupstats.c
index e2836383ed50..0b421a4ca72b 100644
--- a/tools/testing/selftests/acct/cgroupstats.c
+++ b/tools/testing/selftests/acct/cgroupstats.c
@@ -6,139 +6,19 @@
 #include <linux/cgroupstats.h>
 #include <linux/genetlink.h>
 #include <linux/netlink.h>
+#include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/mount.h>
 #include <sys/socket.h>
 #include <sys/types.h>
-#include <sys/mount.h>
-#include <time.h>
 #include <unistd.h>
 
+#include "netlink_helper.h"
 #include "kselftest.h"
 
-#ifndef NLA_ALIGN
-#define NLA_ALIGNTO 4
-#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))
-#define NLA_HDRLEN ((int)NLA_ALIGN(sizeof(struct nlattr)))
-#endif
-
-#define RECV_TIMEOUT_SEC 2
-
-static void *nla_data(const struct nlattr *na)
-{
-	return (void *)((char *)na + NLA_HDRLEN);
-}
-
-static int netlink_open(void)
-{
-	struct timeval tv = { .tv_sec = RECV_TIMEOUT_SEC };
-	struct sockaddr_nl addr = {
-		.nl_family = AF_NETLINK,
-		.nl_pid = getpid(),
-	};
-	int fd;
-
-	fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
-	if (fd < 0)
-		return -errno;
-
-	/*
-	 * Ensure that a missing kernel reply fails the individual test
-	 * case instead of hanging the whole test binary.
-	 */
-	if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
-		int err = -errno;
-
-		close(fd);
-		return err;
-	}
-
-	if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
-		int err = -errno;
-
-		close(fd);
-		return err;
-	}
-
-	return fd;
-}
-
-static int send_request(int fd, void *buf, size_t len)
-{
-	struct sockaddr_nl addr = {
-		.nl_family = AF_NETLINK,
-	};
-
-	if (sendto(fd, buf, len, 0, (struct sockaddr *)&addr, sizeof(addr)) < 0)
-		return -errno;
-
-	return 0;
-}
-
-static int get_family_id(int fd, const char *name)
-{
-	struct {
-		struct nlmsghdr nlh;
-		struct genlmsghdr genl;
-		char buf[256];
-	} req = { 0 };
-	char resp[8192];
-	struct nlmsghdr *nlh;
-	struct genlmsghdr *genl;
-	struct nlattr *na;
-	int len;
-	int rem;
-	int ret;
-
-	req.nlh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
-	req.nlh.nlmsg_type = GENL_ID_CTRL;
-	req.nlh.nlmsg_flags = NLM_F_REQUEST;
-	req.nlh.nlmsg_seq = 1;
-	req.nlh.nlmsg_pid = getpid();
-
-	req.genl.cmd = CTRL_CMD_GETFAMILY;
-	req.genl.version = 1;
-
-	na = (struct nlattr *)((char *)&req + NLMSG_ALIGN(req.nlh.nlmsg_len));
-	na->nla_type = CTRL_ATTR_FAMILY_NAME;
-	na->nla_len = NLA_HDRLEN + strlen(name) + 1;
-	memcpy(nla_data(na), name, strlen(name) + 1);
-	req.nlh.nlmsg_len = NLMSG_ALIGN(req.nlh.nlmsg_len) + NLA_ALIGN(na->nla_len);
-
-	ret = send_request(fd, &req, req.nlh.nlmsg_len);
-	if (ret)
-		return ret;
-
-	len = recv(fd, resp, sizeof(resp), 0);
-	if (len < 0)
-		return -errno;
-
-	for (nlh = (struct nlmsghdr *)resp; NLMSG_OK(nlh, len);
-	     nlh = NLMSG_NEXT(nlh, len)) {
-		if (nlh->nlmsg_type == NLMSG_ERROR) {
-			struct nlmsgerr *err = NLMSG_DATA(nlh);
-
-			return err->error ? err->error : -ENOENT;
-		}
-
-		genl = (struct genlmsghdr *)NLMSG_DATA(nlh);
-		rem = nlh->nlmsg_len - NLMSG_HDRLEN - GENL_HDRLEN;
-		na = (struct nlattr *)((char *)genl + GENL_HDRLEN);
-		while (rem >= (int)sizeof(*na) &&
-		       na->nla_len >= (int)sizeof(*na) &&
-		       na->nla_len <= rem) {
-			if (na->nla_type == CTRL_ATTR_FAMILY_ID)
-				return *(uint16_t *)nla_data(na);
-			rem -= NLA_ALIGN(na->nla_len);
-			na = (struct nlattr *)((char *)na + NLA_ALIGN(na->nla_len));
-		}
-	}
-
-	return -ENOENT;
-}
-
 static int send_cgroupstats_cmd(int fd, int family_id, uint32_t cgroup_fd,
 				int flags)
 {
@@ -203,15 +83,12 @@ static int recv_cgroupstats_response(int fd, struct cgroupstats *stats)
 
 		rem = nlh->nlmsg_len - NLMSG_HDRLEN - GENL_HDRLEN;
 		na = (struct nlattr *)((char *)genl + GENL_HDRLEN);
-		while (rem >= (int)sizeof(*na) &&
-		       na->nla_len >= (int)sizeof(*na) &&
-		       na->nla_len <= rem) {
+		while (nla_ok(na, rem)) {
 			if (na->nla_type == CGROUPSTATS_TYPE_CGROUP_STATS) {
 				memcpy(stats, nla_data(na), sizeof(*stats));
 				return 0;
 			}
-			rem -= NLA_ALIGN(na->nla_len);
-			na = (struct nlattr *)((char *)na + NLA_ALIGN(na->nla_len));
+			na = nla_next(na, &rem);
 		}
 	}
 
diff --git a/tools/testing/selftests/acct/netlink_helper.c b/tools/testing/selftests/acct/netlink_helper.c
new file mode 100644
index 000000000000..3ed834f0e770
--- /dev/null
+++ b/tools/testing/selftests/acct/netlink_helper.c
@@ -0,0 +1,116 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <linux/genetlink.h>
+
+#include "netlink_helper.h"
+
+int netlink_open(void)
+{
+	struct timeval tv = { .tv_sec = ACCT_RCV_TIMEOUT_SEC };
+	struct sockaddr_nl addr = {
+		.nl_family = AF_NETLINK,
+		.nl_pid = getpid(),
+	};
+	int fd;
+
+	fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
+	if (fd < 0)
+		return -errno;
+
+	if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
+		int err = -errno;
+
+		close(fd);
+		return err;
+	}
+
+	if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+		int err = -errno;
+
+		close(fd);
+		return err;
+	}
+
+	return fd;
+}
+
+int send_request(int fd, void *buf, size_t len)
+{
+	struct sockaddr_nl addr = {
+		.nl_family = AF_NETLINK,
+	};
+
+	if (sendto(fd, buf, len, 0, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+		return -errno;
+
+	return 0;
+}
+
+/*
+ * Resolve the generic netlink family ID for @name.
+ * Returns the family ID (>= 0) on success, negative errno on failure.
+ */
+int get_family_id(int fd, const char *name)
+{
+	struct {
+		struct nlmsghdr nlh;
+		struct genlmsghdr genl;
+		char buf[256];
+	} req = { 0 };
+	char resp[8192];
+	struct nlmsghdr *nlh;
+	struct genlmsghdr *genl;
+	struct nlattr *na;
+	int len;
+	int rem;
+	int ret;
+
+	req.nlh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+	req.nlh.nlmsg_type = GENL_ID_CTRL;
+	req.nlh.nlmsg_flags = NLM_F_REQUEST;
+	req.nlh.nlmsg_seq = 1;
+	req.nlh.nlmsg_pid = getpid();
+
+	req.genl.cmd = CTRL_CMD_GETFAMILY;
+	req.genl.version = 1;
+
+	na = (struct nlattr *)((char *)&req + NLMSG_ALIGN(req.nlh.nlmsg_len));
+	na->nla_type = CTRL_ATTR_FAMILY_NAME;
+	na->nla_len = NLA_HDRLEN + strlen(name) + 1;
+	memcpy(nla_data(na), name, strlen(name) + 1);
+	req.nlh.nlmsg_len = NLMSG_ALIGN(req.nlh.nlmsg_len) + NLA_ALIGN(na->nla_len);
+
+	ret = send_request(fd, &req, req.nlh.nlmsg_len);
+	if (ret)
+		return ret;
+
+	len = recv(fd, resp, sizeof(resp), 0);
+	if (len < 0)
+		return -errno;
+
+	for (nlh = (struct nlmsghdr *)resp; NLMSG_OK(nlh, len);
+	     nlh = NLMSG_NEXT(nlh, len)) {
+		if (nlh->nlmsg_type == NLMSG_ERROR) {
+			struct nlmsgerr *err = NLMSG_DATA(nlh);
+
+			return err->error ? err->error : -ENOENT;
+		}
+
+		genl = (struct genlmsghdr *)NLMSG_DATA(nlh);
+		rem = nlh->nlmsg_len - NLMSG_HDRLEN - GENL_HDRLEN;
+		na = (struct nlattr *)((char *)genl + GENL_HDRLEN);
+		while (nla_ok(na, rem)) {
+			if (na->nla_type == CTRL_ATTR_FAMILY_ID)
+				return *(uint16_t *)nla_data(na);
+			na = nla_next(na, &rem);
+		}
+	}
+
+	return -ENOENT;
+}
diff --git a/tools/testing/selftests/acct/netlink_helper.h b/tools/testing/selftests/acct/netlink_helper.h
new file mode 100644
index 000000000000..0320729c4c06
--- /dev/null
+++ b/tools/testing/selftests/acct/netlink_helper.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Shared generic netlink helpers for the acct selftests.
+ */
+#ifndef ACSELFTESTS_ACCT_NETLINK_HELPER_H
+#define ACSELFTESTS_ACCT_NETLINK_HELPER_H
+
+#include <stdbool.h>
+#include <linux/netlink.h>
+
+#ifndef NLA_ALIGNTO
+#define NLA_ALIGNTO 4
+#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))
+#define NLA_HDRLEN ((int)NLA_ALIGN(sizeof(struct nlattr)))
+#endif
+
+/* Fail an individual test case instead of hanging the whole binary. */
+#define ACCT_RCV_TIMEOUT_SEC 2
+
+static inline void *nla_data(const struct nlattr *na)
+{
+	return (void *)((char *)na + NLA_HDRLEN);
+}
+
+static inline bool nla_ok(const struct nlattr *na, int remaining)
+{
+	return remaining >= (int)sizeof(*na) &&
+	       na->nla_len >= sizeof(*na) &&
+	       na->nla_len <= remaining;
+}
+
+static inline struct nlattr *nla_next(const struct nlattr *na, int *remaining)
+{
+	int aligned_len = NLA_ALIGN(na->nla_len);
+
+	*remaining -= aligned_len;
+	return (struct nlattr *)((char *)na + aligned_len);
+}
+
+int netlink_open(void);
+int send_request(int fd, void *buf, size_t len);
+int get_family_id(int fd, const char *name);
+
+#endif /* ACSELFTESTS_ACCT_NETLINK_HELPER_H */
diff --git a/tools/testing/selftests/acct/taskstats_fill_stats_tgid.c b/tools/testing/selftests/acct/taskstats_fill_stats_tgid.c
index d6cab4ae26f2..9a4c1554dee3 100644
--- a/tools/testing/selftests/acct/taskstats_fill_stats_tgid.c
+++ b/tools/testing/selftests/acct/taskstats_fill_stats_tgid.c
@@ -16,14 +16,9 @@
 #include <time.h>
 #include <unistd.h>
 
+#include "netlink_helper.h"
 #include "kselftest.h"
 
-#ifndef NLA_ALIGN
-#define NLA_ALIGNTO 4
-#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))
-#define NLA_HDRLEN ((int)NLA_ALIGN(sizeof(struct nlattr)))
-#endif
-
 #define BUSY_NS (200ULL * 1000 * 1000)
 
 struct worker_ctx {
@@ -35,26 +30,6 @@ struct worker_ctx {
 
 static unsigned long busy_sink;
 
-static void *taskstats_nla_data(const struct nlattr *na)
-{
-	return (void *)((char *)na + NLA_HDRLEN);
-}
-
-static bool taskstats_nla_ok(const struct nlattr *na, int remaining)
-{
-	return remaining >= (int)sizeof(*na) &&
-	       na->nla_len >= sizeof(*na) &&
-	       na->nla_len <= remaining;
-}
-
-static struct nlattr *taskstats_nla_next(const struct nlattr *na, int *remaining)
-{
-	int aligned_len = NLA_ALIGN(na->nla_len);
-
-	*remaining -= aligned_len;
-	return (struct nlattr *)((char *)na + aligned_len);
-}
-
 static uint64_t timespec_diff_ns(const struct timespec *start,
 				 const struct timespec *end)
 {
@@ -84,99 +59,6 @@ static void burn_cpu_for_ns(uint64_t runtime_ns)
 	busy_sink = acc;
 }
 
-static int netlink_open(void)
-{
-	struct sockaddr_nl addr = {
-		.nl_family = AF_NETLINK,
-		.nl_pid = getpid(),
-	};
-	int fd;
-
-	fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
-	if (fd < 0)
-		return -errno;
-
-	if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
-		int err = -errno;
-
-		close(fd);
-		return err;
-	}
-
-	return fd;
-}
-
-static int send_request(int fd, void *buf, size_t len)
-{
-	struct sockaddr_nl addr = {
-		.nl_family = AF_NETLINK,
-	};
-
-	if (sendto(fd, buf, len, 0, (struct sockaddr *)&addr, sizeof(addr)) < 0)
-		return -errno;
-
-	return 0;
-}
-
-static int get_family_id(int fd, const char *name)
-{
-	struct {
-		struct nlmsghdr nlh;
-		struct genlmsghdr genl;
-		char buf[256];
-	} req = { 0 };
-	char resp[8192];
-	struct nlmsghdr *nlh;
-	struct genlmsghdr *genl;
-	struct nlattr *na;
-	int len;
-	int rem;
-	int ret;
-
-	req.nlh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
-	req.nlh.nlmsg_type = GENL_ID_CTRL;
-	req.nlh.nlmsg_flags = NLM_F_REQUEST;
-	req.nlh.nlmsg_seq = 1;
-	req.nlh.nlmsg_pid = getpid();
-
-	req.genl.cmd = CTRL_CMD_GETFAMILY;
-	req.genl.version = 1;
-
-	na = (struct nlattr *)((char *)&req + NLMSG_ALIGN(req.nlh.nlmsg_len));
-	na->nla_type = CTRL_ATTR_FAMILY_NAME;
-	na->nla_len = NLA_HDRLEN + strlen(name) + 1;
-	memcpy(taskstats_nla_data(na), name, strlen(name) + 1);
-	req.nlh.nlmsg_len = NLMSG_ALIGN(req.nlh.nlmsg_len) + NLA_ALIGN(na->nla_len);
-
-	ret = send_request(fd, &req, req.nlh.nlmsg_len);
-	if (ret)
-		return ret;
-
-	len = recv(fd, resp, sizeof(resp), 0);
-	if (len < 0)
-		return -errno;
-
-	for (nlh = (struct nlmsghdr *)resp; NLMSG_OK(nlh, len);
-	     nlh = NLMSG_NEXT(nlh, len)) {
-		if (nlh->nlmsg_type == NLMSG_ERROR) {
-			struct nlmsgerr *err = NLMSG_DATA(nlh);
-
-			return err->error ? err->error : -ENOENT;
-		}
-
-		genl = (struct genlmsghdr *)NLMSG_DATA(nlh);
-		rem = nlh->nlmsg_len - NLMSG_HDRLEN - GENL_HDRLEN;
-		na = (struct nlattr *)((char *)genl + GENL_HDRLEN);
-		while (taskstats_nla_ok(na, rem)) {
-			if (na->nla_type == CTRL_ATTR_FAMILY_ID)
-				return *(uint16_t *)taskstats_nla_data(na);
-			na = taskstats_nla_next(na, &rem);
-		}
-	}
-
-	return -ENOENT;
-}
-
 static int get_taskstats(int fd, int family_id, uint16_t attr_type, uint32_t id,
 			 struct taskstats *stats)
 {
@@ -209,7 +91,7 @@ static int get_taskstats(int fd, int family_id, uint16_t attr_type, uint32_t id,
 	na = (struct nlattr *)((char *)&req + NLMSG_ALIGN(req.nlh.nlmsg_len));
 	na->nla_type = attr_type;
 	na->nla_len = NLA_HDRLEN + sizeof(id);
-	memcpy(taskstats_nla_data(na), &id, sizeof(id));
+	memcpy(nla_data(na), &id, sizeof(id));
 	req.nlh.nlmsg_len = NLMSG_ALIGN(req.nlh.nlmsg_len) + NLA_ALIGN(na->nla_len);
 
 	ret = send_request(fd, &req, req.nlh.nlmsg_len);
@@ -231,21 +113,21 @@ static int get_taskstats(int fd, int family_id, uint16_t attr_type, uint32_t id,
 		genl = (struct genlmsghdr *)NLMSG_DATA(nlh);
 		rem = nlh->nlmsg_len - NLMSG_HDRLEN - GENL_HDRLEN;
 		na = (struct nlattr *)((char *)genl + GENL_HDRLEN);
-		while (taskstats_nla_ok(na, rem)) {
+		while (nla_ok(na, rem)) {
 			if (na->nla_type == TASKSTATS_TYPE_AGGR_PID ||
 			    na->nla_type == TASKSTATS_TYPE_AGGR_TGID) {
-				nested = (struct nlattr *)taskstats_nla_data(na);
+				nested = (struct nlattr *)nla_data(na);
 				nrem = na->nla_len - NLA_HDRLEN;
-				while (taskstats_nla_ok(nested, nrem)) {
+				while (nla_ok(nested, nrem)) {
 					if (nested->nla_type == TASKSTATS_TYPE_STATS) {
-						memcpy(stats, taskstats_nla_data(nested),
+						memcpy(stats, nla_data(nested),
 						       sizeof(*stats));
 						return 0;
 					}
-					nested = taskstats_nla_next(nested, &nrem);
+					nested = nla_next(nested, &nrem);
 				}
 			}
-			na = taskstats_nla_next(na, &rem);
+			na = nla_next(na, &rem);
 		}
 	}
 
-- 
2.43.0


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

* Re: [PATCH] selftests/acct: share netlink helpers
  2026-07-12 17:13 [PATCH] selftests/acct: share netlink helpers Yiyang Chen
@ 2026-07-13  2:34 ` Balbir Singh
  0 siblings, 0 replies; 2+ messages in thread
From: Balbir Singh @ 2026-07-13  2:34 UTC (permalink / raw)
  To: Yiyang Chen; +Cc: Andrew Morton, linux-kernel

On Mon, Jul 13, 2026 at 01:13:31AM +0800, Yiyang Chen wrote:
> Extract the duplicated generic netlink boilerplate (netlink_open,
> send_request, get_family_id, and NLA walker macros) from
> cgroupstats.c and taskstats_fill_stats_tgid.c into a shared
> netlink_helper.{h,c}.
> 
> Suggested-by: Andrew Morton <akpm@linux-foundation.org>
> Signed-off-by: Yiyang Chen <cyyzero16@gmail.com>
> ---
>  tools/testing/selftests/acct/.gitignore       |   1 +
>  tools/testing/selftests/acct/Makefile         |  10 ++
>  tools/testing/selftests/acct/cgroupstats.c    | 133 +----------------
>  tools/testing/selftests/acct/netlink_helper.c | 116 +++++++++++++++
>  tools/testing/selftests/acct/netlink_helper.h |  44 ++++++
>  .../acct/taskstats_fill_stats_tgid.c          | 134 ++----------------
>  6 files changed, 184 insertions(+), 254 deletions(-)
>  create mode 100644 tools/testing/selftests/acct/netlink_helper.c
>  create mode 100644 tools/testing/selftests/acct/netlink_helper.h
> 
> diff --git a/tools/testing/selftests/acct/.gitignore b/tools/testing/selftests/acct/.gitignore
> index 9e9c61c5bfd6..fe0896f54e15 100644
> --- a/tools/testing/selftests/acct/.gitignore
> +++ b/tools/testing/selftests/acct/.gitignore
> @@ -1,4 +1,5 @@
>  acct_syscall
>  taskstats_fill_stats_tgid
> +cgroupstats
>  config
>  process_log
> diff --git a/tools/testing/selftests/acct/Makefile b/tools/testing/selftests/acct/Makefile
> index db88d65f5581..93a11a28a636 100644
> --- a/tools/testing/selftests/acct/Makefile
> +++ b/tools/testing/selftests/acct/Makefile
> @@ -3,7 +3,17 @@ TEST_GEN_PROGS := acct_syscall
>  TEST_GEN_PROGS += taskstats_fill_stats_tgid
>  TEST_GEN_PROGS += cgroupstats
>  
> +NETLINK_HELPER_PROGS := cgroupstats taskstats_fill_stats_tgid
> +
>  CFLAGS += -Wall
>  LDLIBS += -lpthread
>  
>  include ../lib.mk
> +
> +$(NETLINK_HELPER_PROGS): %: %.c netlink_helper.c netlink_helper.h
> +	$(call msg,CC,,$@)
> +	$(Q)$(LINK.c) $< netlink_helper.c $(LDLIBS) -o $@
> +
> +$(addprefix $(OUTPUT)/,$(NETLINK_HELPER_PROGS)): $(OUTPUT)/%: %.c netlink_helper.c netlink_helper.h
> +	$(call msg,CC,,$@)
> +	$(Q)$(LINK.c) $< netlink_helper.c $(LDLIBS) -o $@
> diff --git a/tools/testing/selftests/acct/cgroupstats.c b/tools/testing/selftests/acct/cgroupstats.c
> index e2836383ed50..0b421a4ca72b 100644
> --- a/tools/testing/selftests/acct/cgroupstats.c
> +++ b/tools/testing/selftests/acct/cgroupstats.c
> @@ -6,139 +6,19 @@
>  #include <linux/cgroupstats.h>
>  #include <linux/genetlink.h>
>  #include <linux/netlink.h>
> +#include <stdbool.h>
>  #include <stdint.h>
>  #include <stdio.h>
>  #include <stdlib.h>
>  #include <string.h>
> +#include <sys/mount.h>
>  #include <sys/socket.h>
>  #include <sys/types.h>
> -#include <sys/mount.h>
> -#include <time.h>
>  #include <unistd.h>
>  
> +#include "netlink_helper.h"
>  #include "kselftest.h"
>  
> -#ifndef NLA_ALIGN
> -#define NLA_ALIGNTO 4
> -#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))
> -#define NLA_HDRLEN ((int)NLA_ALIGN(sizeof(struct nlattr)))
> -#endif
> -
> -#define RECV_TIMEOUT_SEC 2
> -
> -static void *nla_data(const struct nlattr *na)
> -{
> -	return (void *)((char *)na + NLA_HDRLEN);
> -}
> -
> -static int netlink_open(void)
> -{
> -	struct timeval tv = { .tv_sec = RECV_TIMEOUT_SEC };
> -	struct sockaddr_nl addr = {
> -		.nl_family = AF_NETLINK,
> -		.nl_pid = getpid(),
> -	};
> -	int fd;
> -
> -	fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
> -	if (fd < 0)
> -		return -errno;
> -
> -	/*
> -	 * Ensure that a missing kernel reply fails the individual test
> -	 * case instead of hanging the whole test binary.
> -	 */
> -	if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
> -		int err = -errno;
> -
> -		close(fd);
> -		return err;
> -	}
> -
> -	if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
> -		int err = -errno;
> -
> -		close(fd);
> -		return err;
> -	}
> -
> -	return fd;
> -}
> -
> -static int send_request(int fd, void *buf, size_t len)
> -{
> -	struct sockaddr_nl addr = {
> -		.nl_family = AF_NETLINK,
> -	};
> -
> -	if (sendto(fd, buf, len, 0, (struct sockaddr *)&addr, sizeof(addr)) < 0)
> -		return -errno;
> -
> -	return 0;
> -}
> -
> -static int get_family_id(int fd, const char *name)
> -{
> -	struct {
> -		struct nlmsghdr nlh;
> -		struct genlmsghdr genl;
> -		char buf[256];
> -	} req = { 0 };
> -	char resp[8192];
> -	struct nlmsghdr *nlh;
> -	struct genlmsghdr *genl;
> -	struct nlattr *na;
> -	int len;
> -	int rem;
> -	int ret;
> -
> -	req.nlh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
> -	req.nlh.nlmsg_type = GENL_ID_CTRL;
> -	req.nlh.nlmsg_flags = NLM_F_REQUEST;
> -	req.nlh.nlmsg_seq = 1;
> -	req.nlh.nlmsg_pid = getpid();
> -
> -	req.genl.cmd = CTRL_CMD_GETFAMILY;
> -	req.genl.version = 1;
> -
> -	na = (struct nlattr *)((char *)&req + NLMSG_ALIGN(req.nlh.nlmsg_len));
> -	na->nla_type = CTRL_ATTR_FAMILY_NAME;
> -	na->nla_len = NLA_HDRLEN + strlen(name) + 1;
> -	memcpy(nla_data(na), name, strlen(name) + 1);
> -	req.nlh.nlmsg_len = NLMSG_ALIGN(req.nlh.nlmsg_len) + NLA_ALIGN(na->nla_len);
> -
> -	ret = send_request(fd, &req, req.nlh.nlmsg_len);
> -	if (ret)
> -		return ret;
> -
> -	len = recv(fd, resp, sizeof(resp), 0);
> -	if (len < 0)
> -		return -errno;
> -
> -	for (nlh = (struct nlmsghdr *)resp; NLMSG_OK(nlh, len);
> -	     nlh = NLMSG_NEXT(nlh, len)) {
> -		if (nlh->nlmsg_type == NLMSG_ERROR) {
> -			struct nlmsgerr *err = NLMSG_DATA(nlh);
> -
> -			return err->error ? err->error : -ENOENT;
> -		}
> -
> -		genl = (struct genlmsghdr *)NLMSG_DATA(nlh);
> -		rem = nlh->nlmsg_len - NLMSG_HDRLEN - GENL_HDRLEN;
> -		na = (struct nlattr *)((char *)genl + GENL_HDRLEN);
> -		while (rem >= (int)sizeof(*na) &&
> -		       na->nla_len >= (int)sizeof(*na) &&
> -		       na->nla_len <= rem) {
> -			if (na->nla_type == CTRL_ATTR_FAMILY_ID)
> -				return *(uint16_t *)nla_data(na);
> -			rem -= NLA_ALIGN(na->nla_len);
> -			na = (struct nlattr *)((char *)na + NLA_ALIGN(na->nla_len));
> -		}
> -	}
> -
> -	return -ENOENT;
> -}
> -
>  static int send_cgroupstats_cmd(int fd, int family_id, uint32_t cgroup_fd,
>  				int flags)
>  {
> @@ -203,15 +83,12 @@ static int recv_cgroupstats_response(int fd, struct cgroupstats *stats)
>  
>  		rem = nlh->nlmsg_len - NLMSG_HDRLEN - GENL_HDRLEN;
>  		na = (struct nlattr *)((char *)genl + GENL_HDRLEN);
> -		while (rem >= (int)sizeof(*na) &&
> -		       na->nla_len >= (int)sizeof(*na) &&
> -		       na->nla_len <= rem) {
> +		while (nla_ok(na, rem)) {
>  			if (na->nla_type == CGROUPSTATS_TYPE_CGROUP_STATS) {
>  				memcpy(stats, nla_data(na), sizeof(*stats));
>  				return 0;
>  			}
> -			rem -= NLA_ALIGN(na->nla_len);
> -			na = (struct nlattr *)((char *)na + NLA_ALIGN(na->nla_len));
> +			na = nla_next(na, &rem);
>  		}
>  	}
>  
> diff --git a/tools/testing/selftests/acct/netlink_helper.c b/tools/testing/selftests/acct/netlink_helper.c
> new file mode 100644
> index 000000000000..3ed834f0e770
> --- /dev/null
> +++ b/tools/testing/selftests/acct/netlink_helper.c
> @@ -0,0 +1,116 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <errno.h>
> +#include <stdint.h>
> +#include <string.h>
> +#include <sys/socket.h>
> +#include <sys/time.h>
> +#include <unistd.h>
> +#include <linux/genetlink.h>
> +
> +#include "netlink_helper.h"
> +
> +int netlink_open(void)
> +{
> +	struct timeval tv = { .tv_sec = ACCT_RCV_TIMEOUT_SEC };
> +	struct sockaddr_nl addr = {
> +		.nl_family = AF_NETLINK,
> +		.nl_pid = getpid(),
> +	};
> +	int fd;
> +
> +	fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
> +	if (fd < 0)
> +		return -errno;
> +
> +	if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
> +		int err = -errno;
> +
> +		close(fd);
> +		return err;
> +	}
> +
> +	if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
> +		int err = -errno;
> +
> +		close(fd);
> +		return err;
> +	}
> +
> +	return fd;
> +}
> +
> +int send_request(int fd, void *buf, size_t len)
> +{
> +	struct sockaddr_nl addr = {
> +		.nl_family = AF_NETLINK,
> +	};
> +
> +	if (sendto(fd, buf, len, 0, (struct sockaddr *)&addr, sizeof(addr)) < 0)
> +		return -errno;
> +
> +	return 0;
> +}
> +
> +/*
> + * Resolve the generic netlink family ID for @name.
> + * Returns the family ID (>= 0) on success, negative errno on failure.
> + */
> +int get_family_id(int fd, const char *name)
> +{
> +	struct {
> +		struct nlmsghdr nlh;
> +		struct genlmsghdr genl;
> +		char buf[256];
> +	} req = { 0 };
> +	char resp[8192];
> +	struct nlmsghdr *nlh;
> +	struct genlmsghdr *genl;
> +	struct nlattr *na;
> +	int len;
> +	int rem;
> +	int ret;
> +
> +	req.nlh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
> +	req.nlh.nlmsg_type = GENL_ID_CTRL;
> +	req.nlh.nlmsg_flags = NLM_F_REQUEST;
> +	req.nlh.nlmsg_seq = 1;
> +	req.nlh.nlmsg_pid = getpid();
> +
> +	req.genl.cmd = CTRL_CMD_GETFAMILY;
> +	req.genl.version = 1;
> +
> +	na = (struct nlattr *)((char *)&req + NLMSG_ALIGN(req.nlh.nlmsg_len));
> +	na->nla_type = CTRL_ATTR_FAMILY_NAME;
> +	na->nla_len = NLA_HDRLEN + strlen(name) + 1;
> +	memcpy(nla_data(na), name, strlen(name) + 1);
> +	req.nlh.nlmsg_len = NLMSG_ALIGN(req.nlh.nlmsg_len) + NLA_ALIGN(na->nla_len);
> +
> +	ret = send_request(fd, &req, req.nlh.nlmsg_len);
> +	if (ret)
> +		return ret;
> +
> +	len = recv(fd, resp, sizeof(resp), 0);
> +	if (len < 0)
> +		return -errno;
> +
> +	for (nlh = (struct nlmsghdr *)resp; NLMSG_OK(nlh, len);
> +	     nlh = NLMSG_NEXT(nlh, len)) {
> +		if (nlh->nlmsg_type == NLMSG_ERROR) {
> +			struct nlmsgerr *err = NLMSG_DATA(nlh);
> +
> +			return err->error ? err->error : -ENOENT;
> +		}
> +
> +		genl = (struct genlmsghdr *)NLMSG_DATA(nlh);
> +		rem = nlh->nlmsg_len - NLMSG_HDRLEN - GENL_HDRLEN;
> +		na = (struct nlattr *)((char *)genl + GENL_HDRLEN);
> +		while (nla_ok(na, rem)) {
> +			if (na->nla_type == CTRL_ATTR_FAMILY_ID)
> +				return *(uint16_t *)nla_data(na);
> +			na = nla_next(na, &rem);
> +		}
> +	}
> +
> +	return -ENOENT;
> +}
> diff --git a/tools/testing/selftests/acct/netlink_helper.h b/tools/testing/selftests/acct/netlink_helper.h
> new file mode 100644
> index 000000000000..0320729c4c06
> --- /dev/null
> +++ b/tools/testing/selftests/acct/netlink_helper.h
> @@ -0,0 +1,44 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Shared generic netlink helpers for the acct selftests.
> + */
> +#ifndef ACSELFTESTS_ACCT_NETLINK_HELPER_H
> +#define ACSELFTESTS_ACCT_NETLINK_HELPER_H
> +
> +#include <stdbool.h>
> +#include <linux/netlink.h>
> +
> +#ifndef NLA_ALIGNTO
> +#define NLA_ALIGNTO 4
> +#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))
> +#define NLA_HDRLEN ((int)NLA_ALIGN(sizeof(struct nlattr)))
> +#endif
> +
> +/* Fail an individual test case instead of hanging the whole binary. */
> +#define ACCT_RCV_TIMEOUT_SEC 2
> +
> +static inline void *nla_data(const struct nlattr *na)
> +{
> +	return (void *)((char *)na + NLA_HDRLEN);
> +}
> +
> +static inline bool nla_ok(const struct nlattr *na, int remaining)
> +{
> +	return remaining >= (int)sizeof(*na) &&
> +	       na->nla_len >= sizeof(*na) &&
> +	       na->nla_len <= remaining;
> +}
> +
> +static inline struct nlattr *nla_next(const struct nlattr *na, int *remaining)
> +{
> +	int aligned_len = NLA_ALIGN(na->nla_len);
> +
> +	*remaining -= aligned_len;
> +	return (struct nlattr *)((char *)na + aligned_len);
> +}
> +
> +int netlink_open(void);
> +int send_request(int fd, void *buf, size_t len);
> +int get_family_id(int fd, const char *name);
> +
> +#endif /* ACSELFTESTS_ACCT_NETLINK_HELPER_H */
> diff --git a/tools/testing/selftests/acct/taskstats_fill_stats_tgid.c b/tools/testing/selftests/acct/taskstats_fill_stats_tgid.c
> index d6cab4ae26f2..9a4c1554dee3 100644
> --- a/tools/testing/selftests/acct/taskstats_fill_stats_tgid.c
> +++ b/tools/testing/selftests/acct/taskstats_fill_stats_tgid.c
> @@ -16,14 +16,9 @@
>  #include <time.h>
>  #include <unistd.h>
>  
> +#include "netlink_helper.h"
>  #include "kselftest.h"
>  
> -#ifndef NLA_ALIGN
> -#define NLA_ALIGNTO 4
> -#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))
> -#define NLA_HDRLEN ((int)NLA_ALIGN(sizeof(struct nlattr)))
> -#endif
> -
>  #define BUSY_NS (200ULL * 1000 * 1000)
>  
>  struct worker_ctx {
> @@ -35,26 +30,6 @@ struct worker_ctx {
>  
>  static unsigned long busy_sink;
>  
> -static void *taskstats_nla_data(const struct nlattr *na)
> -{
> -	return (void *)((char *)na + NLA_HDRLEN);
> -}
> -
> -static bool taskstats_nla_ok(const struct nlattr *na, int remaining)
> -{
> -	return remaining >= (int)sizeof(*na) &&
> -	       na->nla_len >= sizeof(*na) &&
> -	       na->nla_len <= remaining;
> -}
> -
> -static struct nlattr *taskstats_nla_next(const struct nlattr *na, int *remaining)
> -{
> -	int aligned_len = NLA_ALIGN(na->nla_len);
> -
> -	*remaining -= aligned_len;
> -	return (struct nlattr *)((char *)na + aligned_len);
> -}
> -
>  static uint64_t timespec_diff_ns(const struct timespec *start,
>  				 const struct timespec *end)
>  {
> @@ -84,99 +59,6 @@ static void burn_cpu_for_ns(uint64_t runtime_ns)
>  	busy_sink = acc;
>  }
>  
> -static int netlink_open(void)
> -{
> -	struct sockaddr_nl addr = {
> -		.nl_family = AF_NETLINK,
> -		.nl_pid = getpid(),
> -	};
> -	int fd;
> -
> -	fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
> -	if (fd < 0)
> -		return -errno;
> -
> -	if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
> -		int err = -errno;
> -
> -		close(fd);
> -		return err;
> -	}
> -
> -	return fd;
> -}
> -
> -static int send_request(int fd, void *buf, size_t len)
> -{
> -	struct sockaddr_nl addr = {
> -		.nl_family = AF_NETLINK,
> -	};
> -
> -	if (sendto(fd, buf, len, 0, (struct sockaddr *)&addr, sizeof(addr)) < 0)
> -		return -errno;
> -
> -	return 0;
> -}
> -
> -static int get_family_id(int fd, const char *name)
> -{
> -	struct {
> -		struct nlmsghdr nlh;
> -		struct genlmsghdr genl;
> -		char buf[256];
> -	} req = { 0 };
> -	char resp[8192];
> -	struct nlmsghdr *nlh;
> -	struct genlmsghdr *genl;
> -	struct nlattr *na;
> -	int len;
> -	int rem;
> -	int ret;
> -
> -	req.nlh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
> -	req.nlh.nlmsg_type = GENL_ID_CTRL;
> -	req.nlh.nlmsg_flags = NLM_F_REQUEST;
> -	req.nlh.nlmsg_seq = 1;
> -	req.nlh.nlmsg_pid = getpid();
> -
> -	req.genl.cmd = CTRL_CMD_GETFAMILY;
> -	req.genl.version = 1;
> -
> -	na = (struct nlattr *)((char *)&req + NLMSG_ALIGN(req.nlh.nlmsg_len));
> -	na->nla_type = CTRL_ATTR_FAMILY_NAME;
> -	na->nla_len = NLA_HDRLEN + strlen(name) + 1;
> -	memcpy(taskstats_nla_data(na), name, strlen(name) + 1);
> -	req.nlh.nlmsg_len = NLMSG_ALIGN(req.nlh.nlmsg_len) + NLA_ALIGN(na->nla_len);
> -
> -	ret = send_request(fd, &req, req.nlh.nlmsg_len);
> -	if (ret)
> -		return ret;
> -
> -	len = recv(fd, resp, sizeof(resp), 0);
> -	if (len < 0)
> -		return -errno;
> -
> -	for (nlh = (struct nlmsghdr *)resp; NLMSG_OK(nlh, len);
> -	     nlh = NLMSG_NEXT(nlh, len)) {
> -		if (nlh->nlmsg_type == NLMSG_ERROR) {
> -			struct nlmsgerr *err = NLMSG_DATA(nlh);
> -
> -			return err->error ? err->error : -ENOENT;
> -		}
> -
> -		genl = (struct genlmsghdr *)NLMSG_DATA(nlh);
> -		rem = nlh->nlmsg_len - NLMSG_HDRLEN - GENL_HDRLEN;
> -		na = (struct nlattr *)((char *)genl + GENL_HDRLEN);
> -		while (taskstats_nla_ok(na, rem)) {
> -			if (na->nla_type == CTRL_ATTR_FAMILY_ID)
> -				return *(uint16_t *)taskstats_nla_data(na);
> -			na = taskstats_nla_next(na, &rem);
> -		}
> -	}
> -
> -	return -ENOENT;
> -}
> -
>  static int get_taskstats(int fd, int family_id, uint16_t attr_type, uint32_t id,
>  			 struct taskstats *stats)
>  {
> @@ -209,7 +91,7 @@ static int get_taskstats(int fd, int family_id, uint16_t attr_type, uint32_t id,
>  	na = (struct nlattr *)((char *)&req + NLMSG_ALIGN(req.nlh.nlmsg_len));
>  	na->nla_type = attr_type;
>  	na->nla_len = NLA_HDRLEN + sizeof(id);
> -	memcpy(taskstats_nla_data(na), &id, sizeof(id));
> +	memcpy(nla_data(na), &id, sizeof(id));
>  	req.nlh.nlmsg_len = NLMSG_ALIGN(req.nlh.nlmsg_len) + NLA_ALIGN(na->nla_len);
>  
>  	ret = send_request(fd, &req, req.nlh.nlmsg_len);
> @@ -231,21 +113,21 @@ static int get_taskstats(int fd, int family_id, uint16_t attr_type, uint32_t id,
>  		genl = (struct genlmsghdr *)NLMSG_DATA(nlh);
>  		rem = nlh->nlmsg_len - NLMSG_HDRLEN - GENL_HDRLEN;
>  		na = (struct nlattr *)((char *)genl + GENL_HDRLEN);
> -		while (taskstats_nla_ok(na, rem)) {
> +		while (nla_ok(na, rem)) {
>  			if (na->nla_type == TASKSTATS_TYPE_AGGR_PID ||
>  			    na->nla_type == TASKSTATS_TYPE_AGGR_TGID) {
> -				nested = (struct nlattr *)taskstats_nla_data(na);
> +				nested = (struct nlattr *)nla_data(na);
>  				nrem = na->nla_len - NLA_HDRLEN;
> -				while (taskstats_nla_ok(nested, nrem)) {
> +				while (nla_ok(nested, nrem)) {
>  					if (nested->nla_type == TASKSTATS_TYPE_STATS) {
> -						memcpy(stats, taskstats_nla_data(nested),
> +						memcpy(stats, nla_data(nested),
>  						       sizeof(*stats));
>  						return 0;
>  					}
> -					nested = taskstats_nla_next(nested, &nrem);
> +					nested = nla_next(nested, &nrem);
>  				}
>  			}
> -			na = taskstats_nla_next(na, &rem);
> +			na = nla_next(na, &rem);
>  		}
>  	}
>  
> -- 
> 2.43.0
>

Thanks makes sense!

Acked-by: Balbir Singh <balbirs@nvidia.com>


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

end of thread, other threads:[~2026-07-13  2:34 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-07-12 17:13 [PATCH] selftests/acct: share netlink helpers Yiyang Chen
2026-07-13  2:34 ` Balbir Singh

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

Powered by JetHome