File ulp-multithread-test-v7.patch of Package ltp-stable

From fed4557ab273f30c597efdb2dccfb038b9320b80 Mon Sep 17 00:00:00 2001
From: Martin Doucha <mdoucha@suse.cz>
Date: Thu, 7 Jul 2022 12:43:28 +0200
Subject: [PATCH v7] Add test for livepatching large number of threads
To: kernel-qa@suse.de

Status: out of tree
Signed-off-by: Martin Doucha <mdoucha@suse.cz>
---
Changes since v1:
- Rewritten livepatch presence check to use new malloc() protocol
- Updated .save_restore to use flags

Changes since v2:
- Undone .save_restore changes to backport the patch to ltp-stable. Use
  patch v2 for latest Git builds.
 
Changes since v3:
- Rewritten livepatch presence check to detect multiple known livepatch
  packages
- Added .save_restore flags back

Changes since v4:
- Rewritten livepatch presence check to use glibc version string

Changes since v5:
- Rebase for a6a369c5ee ("lib: redefine the overall timeout logic of test")

Changes since v6:
- Fixed timeout setting in tst_test structure

 testcases/ulp/Makefile        |  10 ++
 testcases/ulp/ulp_threads01.c | 199 ++++++++++++++++++++++++++++++++++
 2 files changed, 209 insertions(+)
 create mode 100644 testcases/ulp/Makefile
 create mode 100644 testcases/ulp/ulp_threads01.c

diff --git a/testcases/ulp/Makefile b/testcases/ulp/Makefile
new file mode 100644
index 000000000..5b5763136
--- /dev/null
+++ b/testcases/ulp/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) International Business Machines  Corp., 2001
+
+top_srcdir		?= ../..
+
+include $(top_srcdir)/include/mk/testcases.mk
+
+CFLAGS += -pthread
+
+include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/ulp/ulp_threads01.c b/testcases/ulp/ulp_threads01.c
new file mode 100644
index 000000000..c456078cf
--- /dev/null
+++ b/testcases/ulp/ulp_threads01.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2022 SUSE LLC <mdoucha@suse.cz>
+ */
+/*\
+ * [Description]
+ *
+ * Create a large number of threads and wait for the process to be livepatched.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <time.h>
+#include <pthread.h>
+#include <sys/resource.h>
+#include <gnu/libc-version.h>
+
+#include "tst_test.h"
+#include "tst_safe_pthread.h"
+
+#define LIVEPATCH_MAGIC "-lp-"
+
+#define SAFETY_MARGIN 4096
+#define TESTDATA_SIZE 256
+#define THREAD_LIMIT_PATH "/proc/sys/kernel/threads-max"
+
+#define CHECK_PTHREAD(SCALL)						\
+	do {								\
+		TST_ERR = SCALL;					\
+									\
+		if (TST_ERR)						\
+			tst_brk(TBROK | TTERRNO, #SCALL " failed");	\
+	} while (0)
+
+static char *thcount_str, *delay_str, *patchinfo;
+static long th_count = 16, delay_ms;
+static volatile int quit;
+static pthread_t *tid_list;
+static pthread_barrier_t barrier;
+static int barrier_init;
+static unsigned char testdata[TESTDATA_SIZE];
+static struct timespec delay;
+
+void handle_sigusr1(int sig LTP_ATTRIBUTE_UNUSED)
+{
+	quit = 1;
+}
+
+static const char *get_livepatch_version(void)
+{
+	const char *ret, *libc_version = gnu_get_libc_version();
+
+	if (!libc_version)
+		tst_brk(TBROK, "Glibc version is NULL");
+
+	tst_res(TINFO, "Glibc version: %s", libc_version);
+	ret = strstr(libc_version, LIVEPATCH_MAGIC);
+	return ret ? ret + strlen(LIVEPATCH_MAGIC) : NULL;
+}
+
+void setup(void)
+{
+	struct rlimit lim;
+	long i, max_threads;
+
+	if (thcount_str && tst_parse_long(thcount_str, &th_count, 1, LONG_MAX))
+		tst_brk(TBROK, "Invalid thread count: %s", thcount_str);
+
+	if (delay_str && tst_parse_long(delay_str, &delay_ms, 0, LONG_MAX))
+		tst_brk(TBROK, "Invalid delay value: %s", delay_str);
+
+	delay.tv_sec = delay_ms / 1000;
+	delay.tv_nsec = (delay_ms % 1000) * 1000000;
+	SAFE_FILE_SCANF(THREAD_LIMIT_PATH, "%ld", &max_threads);
+	SAFE_GETRLIMIT(RLIMIT_NPROC, &lim);
+
+	if (th_count + SAFETY_MARGIN > max_threads) {
+		if (access(THREAD_LIMIT_PATH, W_OK)) {
+			tst_brk(TCONF | TERRNO,
+				"Cannot increase max thread count to %ld",
+				th_count + SAFETY_MARGIN);
+		}
+
+		SAFE_FILE_PRINTF(THREAD_LIMIT_PATH, "%ld",
+			th_count + SAFETY_MARGIN);
+	}
+
+	if (th_count + SAFETY_MARGIN > (long)lim.rlim_cur) {
+		if (th_count + SAFETY_MARGIN > (long)lim.rlim_max)
+			lim.rlim_max = th_count + SAFETY_MARGIN;
+
+		lim.rlim_cur = lim.rlim_max;
+		SAFE_SETRLIMIT(RLIMIT_NPROC, &lim);
+	}
+
+	CHECK_PTHREAD(pthread_barrier_init(&barrier, NULL, th_count + 1));
+	barrier_init = 1;
+
+	for (i = 0; i < TESTDATA_SIZE; i++)
+		testdata[i] = i;
+}
+
+void *thread_main(void *arg)
+{
+	unsigned char *buf;
+	int i;
+
+	pthread_barrier_wait(&barrier);
+
+	while (!quit) {
+		buf = SAFE_MALLOC(TESTDATA_SIZE * sizeof(char));
+		memcpy(buf, arg, TESTDATA_SIZE * sizeof(char));
+
+		for (i = 0; i < TESTDATA_SIZE; i++) {
+			if (buf[i] != (unsigned char)i) {
+				tst_brk(TBROK, "Wrong testdata value: %u != %u",
+					buf[i], (unsigned char)i);
+			}
+		}
+
+		free(buf);
+
+		if (delay_ms)
+			nanosleep(&delay, NULL);
+	}
+
+	return arg;
+}
+
+void run(void)
+{
+	long i;
+	pthread_attr_t attr;
+	const char *patchver;
+
+	if (get_livepatch_version())
+		tst_brk(TBROK, "Glibc is already livepatched");
+
+	/* Minimize performance impact on system */
+	CHECK_PTHREAD(pthread_attr_init(&attr));
+	CHECK_PTHREAD(pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN));
+
+	if (setpriority(PRIO_PROCESS, 0, 19))
+		tst_brk(TBROK | TERRNO, "setpriority() failed");
+
+	tid_list = SAFE_MALLOC(th_count * sizeof(pthread_t));
+
+	for (i = 0; i < th_count; i++)
+		SAFE_PTHREAD_CREATE(tid_list + i, &attr, thread_main, testdata);
+
+	pthread_attr_destroy(&attr);
+	signal(SIGUSR1, handle_sigusr1);
+	tst_res(TINFO, "PID %d ready, waiting for signal SIGUSR1", getpid());
+	pthread_barrier_wait(&barrier);
+
+	for (i = 0; i < th_count; i++)
+		SAFE_PTHREAD_JOIN(tid_list[i], NULL);
+
+	tst_res(TPASS, "All threads terminated successfully");
+	free(tid_list);
+	tid_list = NULL;
+	patchver = get_livepatch_version();
+
+	if (patchver)
+		tst_res(TPASS, "Glibc livepatch version: %s", patchver);
+	else
+		tst_res(TFAIL, "No Glibc livepatch found");
+}
+
+void cleanup(void)
+{
+	free(tid_list);
+	free(patchinfo);
+
+	if (barrier_init)
+		CHECK_PTHREAD(pthread_barrier_destroy(&barrier));
+}
+
+static struct tst_test test = {
+	.test_all = run,
+	.setup = setup,
+	.cleanup = cleanup,
+	.needs_root = 1,
+	.timeout = TST_UNLIMITED_TIMEOUT,
+	.options = (struct tst_option[]) {
+		{"s:", &delay_str,
+			"Thread sleep length in milliseconds (default: 0ms)"},
+		{"t:", &thcount_str,
+			"Number of threads to create (default: 16)"},
+		{}
+	},
+	.save_restore = (const struct tst_path_val[]){
+		{"/proc/sys/kernel/threads-max", NULL,
+			TST_SR_TCONF_MISSING | TST_SR_SKIP_RO},
+		{}
+	}
+};
-- 
2.49.0

openSUSE Build Service is sponsored by