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