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