1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2018 Google, Inc.
4 *
5 * tgkill() delivers a signal to a specific thread.  Test this by installing
6 * a SIGUSR1 handler which records the current pthread ID.  Start a number
7 * of threads in parallel, then one-by-one call tgkill(..., tid, SIGUSR1)
8 * and check that the expected pthread ID was recorded.
9 */
10
11#include <pthread.h>
12#include <stdlib.h>
13
14#include "tst_safe_pthread.h"
15#include "tst_test.h"
16#include "tgkill.h"
17
18struct thread_state {
19	pthread_t thread;
20	pid_t tid;
21};
22
23static char *str_threads;
24static int n_threads = 10;
25static struct thread_state *threads;
26
27static pthread_t sigusr1_thread;
28
29static void sigusr1_handler(int signum __attribute__((unused)))
30{
31	sigusr1_thread = pthread_self();
32}
33
34static void *thread_func(void *arg)
35{
36	struct thread_state *thread = arg;
37
38	/**
39	 * There is no standard way to map pthread -> tid, so we will have the
40	 * child stash its own tid then notify the parent that the stashed tid
41	 * is available.
42	 */
43	thread->tid = sys_gettid();
44
45	TST_CHECKPOINT_WAKE(0);
46
47	TST_CHECKPOINT_WAIT(1);
48
49	return arg;
50}
51
52static void start_thread(struct thread_state *thread)
53{
54	SAFE_PTHREAD_CREATE(&thread->thread, NULL, thread_func, thread);
55
56	TST_CHECKPOINT_WAIT(0);
57}
58
59static void stop_threads(void)
60{
61	int i;
62
63	TST_CHECKPOINT_WAKE2(1, n_threads);
64
65	for (i = 0; i < n_threads; i++) {
66		if (threads[i].tid == -1)
67			continue;
68
69		SAFE_PTHREAD_JOIN(threads[i].thread, NULL);
70		threads[i].tid = -1;
71	}
72
73	if (threads)
74		free(threads);
75}
76
77static void run(void)
78{
79	int i;
80
81	for (i = 0; i < n_threads; i++) {
82		sigusr1_thread = pthread_self();
83
84		TEST(sys_tgkill(getpid(), threads[i].tid, SIGUSR1));
85		if (TST_RET) {
86			tst_res(TFAIL | TTERRNO, "tgkill() failed");
87			return;
88		}
89
90		while (pthread_equal(sigusr1_thread, pthread_self()))
91			usleep(1000);
92
93		if (!pthread_equal(sigusr1_thread, threads[i].thread)) {
94			tst_res(TFAIL, "SIGUSR1 delivered to wrong thread");
95			return;
96		}
97	}
98
99	tst_res(TPASS, "SIGUSR1 delivered to correct threads");
100}
101
102static void setup(void)
103{
104	int i;
105
106	if (tst_parse_int(str_threads, &n_threads, 1, INT_MAX))
107		tst_brk(TBROK, "Invalid number of threads '%s'", str_threads);
108
109	threads = SAFE_MALLOC(sizeof(*threads) * n_threads);
110
111	struct sigaction sigusr1 = {
112		.sa_handler = sigusr1_handler,
113	};
114	SAFE_SIGACTION(SIGUSR1, &sigusr1, NULL);
115
116	for (i = 0; i < n_threads; i++)
117		threads[i].tid = -1;
118
119	for (i = 0; i < n_threads; i++)
120		start_thread(&threads[i]);
121}
122
123static struct tst_test test = {
124	.options = (struct tst_option[]) {
125		{"t:", &str_threads, "Number of threads (default 10)"},
126		{}
127	},
128	.needs_checkpoints = 1,
129	.setup = setup,
130	.test_all = run,
131	.cleanup = stop_threads,
132};
133