1/*
2 * Copyright (c) 2015, Cyril Hrubis <chrubis@suse.cz>
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of version 2 of the GNU General Public License as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it would be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 *
12 * You should have received a copy of the GNU General Public License along
13 * with this program; if not, write the Free Software Foundation, Inc.,
14 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
15 *
16 * This sample test aims to check the following assertion:
17 *
18 * pthread_create creates a thread with attributes as specified in the attr parameter.
19 *
20 * This test tests scheduller attributes are set correctly and schedulling works.
21 *
22 * The steps are:
23 *
24 *  - create thread with given scheduler policy and minimal priority for the
25 *    scheduling policy
26 *
27 *  - get the scheduler attributes of the running thread and check
28 *    that they are set as requested
29 *
30 *  - start a thread(s) with higher priority and check that the thread with
31 *    lower priority does not finish until the high priority threads finished
32 */
33
34 /* We are testing conformance to IEEE Std 1003.1, 2003 Edition */
35#define _POSIX_C_SOURCE 200112L
36
37/* Must be included first */
38#include "affinity.h"
39
40#include <pthread.h>
41#include <stdio.h>
42#include <string.h>
43#include <unistd.h>
44#include <signal.h>
45#include <unistd.h>
46#include <stdlib.h>
47#include <sys/time.h>
48#include "posixtest.h"
49#include "ncpu.h"
50
51static volatile sig_atomic_t flag;
52static int n_threads;
53
54static void alarm_handler()
55{
56	flag = 0;
57}
58
59void *do_work(void *arg)
60{
61	(void) arg;
62
63	while (flag)
64		sched_yield();
65
66	return NULL;
67}
68
69static void init_attr(pthread_attr_t *attr, int sched_policy, int prio)
70{
71	struct sched_param sched_param = {.sched_priority = prio};
72	int ret;
73
74	ret = pthread_attr_init(attr);
75	if (ret) {
76		fprintf(stderr, "pthread_attr_init(): %s\n", strerror(ret));
77		exit(PTS_UNRESOLVED);
78	}
79
80	ret = pthread_attr_setschedpolicy(attr, sched_policy);
81	if (ret) {
82		fprintf(stderr, "pthread_setschedpolicy(): %s\n", strerror(ret));
83		exit(PTS_UNRESOLVED);
84	}
85
86	ret = pthread_attr_setinheritsched(attr, PTHREAD_EXPLICIT_SCHED);
87	if (ret) {
88		fprintf(stderr, "pthread_attr_setinheritsched(): %s\n", strerror(ret));
89		exit(PTS_UNRESOLVED);
90	}
91
92	ret = pthread_attr_setschedparam(attr, &sched_param);
93	if (ret) {
94		fprintf(stderr, "pthread_attr_setschedparam(): %s\n", strerror(ret));
95		exit(PTS_UNRESOLVED);
96	}
97}
98
99static void run_hp_threads(int sched_policy, int sched_prio)
100{
101	struct itimerval it;
102	pthread_t threads[n_threads];
103	pthread_attr_t attr;
104	int i, ret;
105
106	flag = 1;
107
108	it.it_interval.tv_sec = 0;
109	it.it_interval.tv_usec = 0;
110	it.it_value.tv_sec = n_threads / 20;
111	it.it_value.tv_usec = (n_threads % 20) * 50000;
112
113	init_attr(&attr, sched_policy, sched_prio);
114
115	if (signal(SIGPROF, alarm_handler) == SIG_ERR) {
116		perror("signal()");
117		exit(PTS_UNRESOLVED);
118	}
119
120	if (setitimer(ITIMER_PROF, &it, NULL)) {
121		perror("setitimer(ITIMER_VIRTUAL, ...)");
122		exit(PTS_UNRESOLVED);
123	}
124
125	for (i = 0; i < n_threads; i++) {
126		ret = pthread_create(&threads[i], &attr, do_work, NULL);
127		if (ret) {
128			fprintf(stderr, "pthread_create(): %s\n",
129			        strerror(ret));
130			exit(PTS_UNRESOLVED);
131		}
132	}
133
134	if (flag) {
135		printf("FAILED: low priority thread scheduled\n");
136		exit(PTS_FAIL);
137	}
138
139	pthread_attr_destroy(&attr);
140
141	for (i = 0; i < n_threads; i++)
142		pthread_join(threads[i], NULL);
143
144}
145
146struct params {
147	int sched_policy;
148	int sched_priority;
149};
150
151static void *do_test(void *arg)
152{
153	int ret, sched_policy;
154	struct sched_param param;
155	struct params *p = arg;
156
157	/* First check that the scheduler parameters are set correctly */
158	ret = pthread_getschedparam(pthread_self(), &sched_policy, &param);
159	if (ret) {
160		fprintf(stderr, "pthread_getschedparam(): %s\n", strerror(ret));
161		exit(PTS_UNRESOLVED);
162	}
163
164	if (p->sched_policy != sched_policy) {
165		printf("FAILED: have scheduler policy %i expected %i\n",
166		       sched_policy, p->sched_policy);
167		exit(PTS_FAIL);
168	}
169
170	if (p->sched_priority != param.sched_priority) {
171		printf("FAILED: have scheduler priority %i expected %i\n",
172		       p->sched_priority, param.sched_priority);
173		exit(PTS_FAIL);
174	}
175
176	/* Now check that priorities actually work */
177	run_hp_threads(p->sched_policy, p->sched_priority + 1);
178
179	return NULL;
180}
181
182struct tcase {
183	int sched_policy;
184	int prio;
185};
186
187enum tprio {
188	MIN,
189	HALF,
190	MAX_1,
191};
192
193struct tcase tcases[] = {
194	{SCHED_FIFO, MIN},
195	{SCHED_FIFO, HALF},
196	{SCHED_FIFO, MAX_1},
197	{SCHED_RR, MIN},
198	{SCHED_RR, HALF},
199	{SCHED_RR, MAX_1},
200};
201
202static int get_prio(struct tcase *self)
203{
204	switch (self->prio) {
205	case MIN:
206		return sched_get_priority_min(self->sched_policy);
207	break;
208	case HALF:
209		 return (sched_get_priority_min(self->sched_policy) +
210		         sched_get_priority_max(self->sched_policy)) / 2;
211	break;
212	case MAX_1:
213		return sched_get_priority_max(self->sched_policy) - 1;
214	break;
215	}
216
217	printf("Wrong self->prio %i\n", self->prio);
218	exit(PTS_UNRESOLVED);
219}
220
221static const char *sched_policy_name(int policy)
222{
223	switch (policy) {
224	case SCHED_FIFO:
225		return "SCHED_FIFO";
226	case SCHED_RR:
227		return "SCHED_RR";
228	default:
229		return "UNKNOWN";
230	}
231}
232
233int main(void)
234{
235	pthread_attr_t attr;
236	pthread_t th;
237	struct params p;
238	int ret;
239	unsigned int i;
240
241	ret = set_affinity_single();
242	if (ret) {
243		n_threads = get_ncpu();
244		if (n_threads == -1) {
245			printf("Cannot get number of CPUs\n");
246			return PTS_UNRESOLVED;
247		}
248		printf("INFO: Affinity not supported, running %i threads.\n",
249		       n_threads);
250	} else {
251		printf("INFO: Affinity works, will use only one thread.\n");
252		n_threads = 1;
253	}
254
255	for (i = 0; i < ARRAY_SIZE(tcases); i++) {
256		p.sched_policy = tcases[i].sched_policy;
257		p.sched_priority = get_prio(&tcases[i]);
258
259		init_attr(&attr, p.sched_policy, p.sched_priority);
260
261		printf("INFO: Testing %s prio %i\n",
262		       sched_policy_name(p.sched_policy), p.sched_priority);
263
264		ret = pthread_create(&th, &attr, do_test, &p);
265		if (ret) {
266			fprintf(stderr, "pthread_create(): %s\n", strerror(ret));
267			return PTS_UNRESOLVED;
268		}
269
270		pthread_join(th, NULL);
271
272		pthread_attr_destroy(&attr);
273	}
274
275	printf("Test PASSED\n");
276	return 0;
277}
278