1 /*
2  * Copyright (C) 2017-2021 Canonical, Ltd.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * This code is a complete clean re-write of the stress tool by
19  * Colin Ian King <colin.king@canonical.com> and attempts to be
20  * backwardly compatible with the stress tool by Amos Waterland
21  * <apw@rossby.metr.ou.edu> but has more stress tests and more
22  * functionality.
23  *
24  */
25 #include "stress-ng.h"
26 
27 static const stress_help_t help[] = {
28 	{ NULL,	"netlink-task N",	"start N workers exercising netlink tasks events" },
29 	{ NULL,	"netlink-task-ops N",	"stop netlink-task workers after N bogo events" },
30 	{ NULL,	NULL,			NULL }
31 };
32 
33 #if defined (__linux__) && 		\
34     defined(HAVE_LINUX_CONNECTOR_H) &&	\
35     defined(HAVE_LINUX_NETLINK_H) &&	\
36     defined(HAVE_LINUX_CN_PROC_H) &&	\
37     defined(HAVE_LINUX_GENETLINK_H) &&	\
38     defined(HAVE_LINUX_TASKSTATS_H)
39 
40 #define NLA_DATA(na)            ((void *)((char*)(na) + NLA_HDRLEN))
41 #define NLA_PAYLOAD(len)        ((len) - NLA_HDRLEN)
42 
43 #define GENL_MSG_DATA(glh)       ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN))
44 #define GENL_MSG_PAYLOAD(glh)    (NLMSG_PAYLOAD(glh, 0) - GENL_HDRLEN)
45 
46 /*
47  *  netlink message with 1K payload
48  */
49 typedef struct {
50 	struct nlmsghdr n;
51 	struct genlmsghdr g;
52 	char data[1024];	/* cppcheck-suppress unusedStructMember */
53 } stress_nlmsg_t;
54 
55 
56 /*
57  *  stress_netlink_task_supported()
58  *	check if we can run this with SHIM_CAP_NET_ADMIN capability
59  */
stress_netlink_task_supported(const char * name)60 static int stress_netlink_task_supported(const char *name)
61 {
62 	if (!stress_check_capability(SHIM_CAP_NET_ADMIN)) {
63 		pr_inf_skip("%s stressor will be skipped, "
64 			"need to be running with CAP_NET_ADMIN "
65 			"rights for this stressor\n", name);
66 		return -1;
67 	}
68 	return 0;
69 }
70 
71 /*
72  *  stress_netlink_sendcmd()
73  *	send a netlink cmd
74  */
stress_netlink_sendcmd(const stress_args_t * args,const int sock,const uint16_t nlmsg_type,const uint16_t nlmsg_pid,const uint16_t cmd,const uint16_t nla_type,const void * nla_data,const int nla_len)75 static int stress_netlink_sendcmd(
76 	const stress_args_t *args,
77 	const int sock,
78 	const uint16_t nlmsg_type,
79 	const uint16_t nlmsg_pid,
80 	const uint16_t cmd,
81 	const uint16_t nla_type,
82 	const void *nla_data,
83 	const int nla_len)
84 {
85 	struct nlattr *na;
86 	char *nlmsgbuf;
87 	ssize_t nlmsgbuf_len;
88 	struct sockaddr_nl addr;
89 	stress_nlmsg_t nlmsg;
90 
91 	nlmsg.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
92 	nlmsg.n.nlmsg_type = nlmsg_type;
93 	nlmsg.n.nlmsg_flags = NLM_F_REQUEST;
94 	nlmsg.n.nlmsg_pid = nlmsg_pid;
95 	nlmsg.n.nlmsg_seq = 0;
96 	nlmsg.g.cmd = cmd;
97 	nlmsg.g.version = 0x1;
98 
99 	na = (struct nlattr *)GENL_MSG_DATA(&nlmsg);
100 	na->nla_type = nla_type;
101 	na->nla_len = nla_len + NLA_HDRLEN;
102 	(void)memcpy(NLA_DATA(na), nla_data, (size_t)nla_len);
103 	nlmsg.n.nlmsg_len += NLMSG_ALIGN(na->nla_len);
104 
105 	nlmsgbuf = (char *)&nlmsg;
106 	nlmsgbuf_len = nlmsg.n.nlmsg_len;
107 
108 	(void)memset(&addr, 0, sizeof(addr));
109 	addr.nl_family = AF_NETLINK;
110 
111 	while (nlmsgbuf_len > 0) {
112 		ssize_t len;
113 
114 		len = sendto(sock, nlmsgbuf, (size_t)nlmsgbuf_len, 0,
115 			(struct sockaddr *)&addr, sizeof(addr));
116 		if ((len < 0) &&
117 		    (errno != EAGAIN) &&
118 		    (errno != EINTR)) {
119 			pr_fail("%s: sendto failed: %d (%s)\n",
120 				args->name, errno, strerror(errno));
121 			return -1;
122 		}
123 		nlmsgbuf_len -= len;
124 		nlmsgbuf += len;
125 	}
126 	return 0;
127 }
128 
129 /*
130  *  stress_parse_payload()
131  *	parse the aggregated message payload and sanity
132  * 	check that the pid matches and involuntary context
133  *	switches are incrementing over time.
134  */
stress_parse_payload(const stress_args_t * args,struct nlattr * na,const pid_t pid,uint64_t * nivcsw)135 static void stress_parse_payload(
136 	const stress_args_t *args,
137 	struct nlattr *na,
138 	const pid_t pid,
139 	uint64_t *nivcsw)
140 {
141 	const ssize_t total_len = NLA_PAYLOAD(na->nla_len);
142 	ssize_t len;
143 
144 	na = (struct nlattr *)NLA_DATA(na);
145 	for (len = 0; len < total_len; ) {
146 		const ssize_t new_len = NLA_ALIGN(na->nla_len);
147 		struct taskstats *t;
148 		pid_t task_pid;
149 
150 		switch (na->nla_type) {
151 		case TASKSTATS_TYPE_PID:
152 			task_pid = *(pid_t *)NLA_DATA(na);
153 			if (task_pid != pid) {
154 				pr_fail("%s: TASKSTATS_TYPE_PID got pid %" PRIdMAX ", "
155 					"expected %" PRIdMAX "\n",
156 					args->name,
157 					(intmax_t)task_pid, (intmax_t)pid);
158 			}
159 			break;
160 		case TASKSTATS_TYPE_STATS:
161 			t = (struct taskstats *)NLA_DATA(na);
162 			if ((uint64_t)t->nivcsw < *nivcsw) {
163 				pr_fail("%s: TASKSTATS_TYPE_STATS got %"
164 					PRIu64 " involuntary context switches, "
165 					"expected at least %" PRIu64 "\n",
166 					args->name, (uint64_t)t->nivcsw, *nivcsw);
167 			}
168 			*nivcsw = (uint64_t)t->nivcsw;
169 			break;
170 		}
171 		len += new_len;
172 		na = (struct nlattr *)((char *)na + new_len);
173 	}
174 }
175 
176 /*
177  *   stress_netlink_taskstats_monitor()
178  *	monitor parent's activity using taskstats info
179  */
stress_netlink_taskstats_monitor(const stress_args_t * args,const int sock,const pid_t pid,const uint16_t id,uint64_t * nivcsw)180 static int stress_netlink_taskstats_monitor(
181 	const stress_args_t *args,
182 	const int sock,
183 	const pid_t pid,
184 	const uint16_t id,
185 	uint64_t *nivcsw)
186 {
187 	do {
188 		stress_nlmsg_t msg;
189 		ssize_t msg_len, len;
190 		int ret;
191 		pid_t pid_data = pid;
192 		struct nlattr *na;
193 
194 		ret = stress_netlink_sendcmd(args, sock, id, pid, TASKSTATS_CMD_GET,
195 			TASKSTATS_CMD_ATTR_PID, &pid_data, sizeof(pid_data));
196 		if (ret < 0) {
197 			pr_err("%s: sendto TASKSTATS_CMD_GET failed: %d (%s)\n",
198 				args->name, errno, strerror(errno));
199 			break;
200 		}
201 
202 		(void)memset(&msg, 0, sizeof(msg));
203 		msg_len = recv(sock, &msg, sizeof(msg), 0);
204 		if (msg_len < 0)
205 			continue;
206 
207 		if (!NLMSG_OK((&msg.n), (unsigned int)msg_len)) {
208 			pr_fail("%s: recv failed: %d (%s)\n",
209 				args->name, errno, strerror(errno));
210 			break;
211 		}
212 		msg_len = GENL_MSG_PAYLOAD(&msg.n);
213 		na = (struct nlattr *)GENL_MSG_DATA(&msg);
214 
215 		for (len = 0; len < msg_len; len += NLA_ALIGN(na->nla_len)) {
216 			if (na->nla_type == TASKSTATS_TYPE_AGGR_PID)
217 				stress_parse_payload(args, na, pid, nivcsw);
218 		}
219 		inc_counter(args);
220 	} while (keep_stressing(args));
221 
222 	return 0;
223 }
224 
225 /*
226  *  stress_netlink_task()
227  *	stress netlink task events
228  */
stress_netlink_task(const stress_args_t * args)229 static int stress_netlink_task(const stress_args_t *args)
230 {
231 	int ret, sock = -1;
232 	ssize_t len;
233 	struct sockaddr_nl addr;
234 	struct nlattr *na;
235 	stress_nlmsg_t nlmsg;
236 	const pid_t pid = getpid();
237 	uint16_t id;
238 	uint64_t nivcsw = 0ULL;	/* number of involuntary context switches */
239 	static const char name[] = TASKSTATS_GENL_NAME;
240 
241 	if ((sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC)) < 0) {
242 		if (errno == EPROTONOSUPPORT) {
243 			pr_err("%s: kernel does not support netlink, errno=%d (%s)\n",
244 				args->name, errno, strerror(errno));
245 			return EXIT_NO_RESOURCE;
246 		}
247 		pr_fail("%s: socket failed: errno=%d (%s)\n",
248 			args->name, errno, strerror(errno));
249 		return EXIT_FAILURE;
250 	}
251 
252 	(void)memset(&addr, 0, sizeof(addr));
253 	addr.nl_family = AF_NETLINK;
254 
255 	if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
256 		pr_err("%s: bind failed: errno=%d (%s)\n",
257 			args->name, errno, strerror(errno));
258 		(void)close(sock);
259 		return EXIT_FAILURE;
260 	}
261 
262 	ret = stress_netlink_sendcmd(args, sock, GENL_ID_CTRL, pid, CTRL_CMD_GETFAMILY,
263 		CTRL_ATTR_FAMILY_NAME, (const void *)name, sizeof(name));
264 	if (ret < 0) {
265 		pr_err("%s: sendto CTRL_CMD_GETFAMILY failed: %d (%s)\n",
266 			args->name, errno, strerror(errno));
267 	}
268 	len = recv(sock, &nlmsg, sizeof(nlmsg), 0);
269 	if (len < 0) {
270 		pr_err("%s: recv failed: %d (%s)\n",
271 			args->name, errno, strerror(errno));
272 		(void)close(sock);
273 		return EXIT_FAILURE;
274 	}
275 	if (!NLMSG_OK((&nlmsg.n), (unsigned int)len)) {
276 		pr_err("%s: recv NLMSG error\n",
277 			args->name);
278 		(void)close(sock);
279 		return EXIT_FAILURE;
280 	}
281 	na = (struct nlattr *)GENL_MSG_DATA(&nlmsg);
282 	na = (struct nlattr *)((char *) na + NLA_ALIGN(na->nla_len));
283 	if (na->nla_type == CTRL_ATTR_FAMILY_ID) {
284 		uint16_t *id_ptr = (uint16_t *)NLA_DATA(na);
285 
286 		id = *id_ptr;
287 	} else {
288 		pr_err("%s: failed to get family id\n", args->name);
289 		(void)close(sock);
290 		return EXIT_FAILURE;
291 	}
292 
293 	stress_set_proc_state(args->name, STRESS_STATE_RUN);
294 
295 	do {
296 		if (stress_netlink_taskstats_monitor(args, sock, pid, id, &nivcsw) < 0)
297 			break;
298 	} while (keep_stressing(args));
299 
300 	stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
301 #if 0
302 	/* Some statistics */
303 	pr_inf("%s: process %d has %" PRIu64 " involuntary context switches\n",
304 		args->name, pid, nivcsw);
305 #endif
306 	(void)close(sock);
307 
308 	return EXIT_SUCCESS;
309 }
310 
311 stressor_info_t stress_netlink_task_info = {
312 	.stressor = stress_netlink_task,
313 	.supported = stress_netlink_task_supported,
314 	.class = CLASS_SCHEDULER | CLASS_OS,
315 	.help = help
316 };
317 #else
318 stressor_info_t stress_netlink_task_info = {
319 	.stressor = stress_not_implemented,
320 	.class = CLASS_SCHEDULER | CLASS_OS,
321 	.help = help
322 };
323 #endif
324