blob: fe151c84db299b461d11e4133cadfffdd0cbdc03 [file] [log] [blame]
/*
* Copyright (c) 2015, Cyril Hrubis <chrubis@suse.cz>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This sample test aims to check the following assertion:
*
* pthread_create creates a thread with attributes as specified in the attr parameter.
*
* This test tests scheduller attributes are set correctly and schedulling works.
*
* The steps are:
*
* - create thread with given scheduler policy and minimal priority for the
* scheduling policy
*
* - get the scheduler attributes of the running thread and check
* that they are set as requested
*
* - start a thread(s) with higher priority and check that the thread with
* lower priority does not finish until the high priority threads finished
*/
/* Must be included first */
#include "affinity.h"
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/time.h>
#include "posixtest.h"
#include "ncpu.h"
static volatile sig_atomic_t flag;
static int n_threads;
static void alarm_handler()
{
flag = 0;
}
void *do_work(void *arg)
{
(void) arg;
while (flag)
sched_yield();
return NULL;
}
static void init_attr(pthread_attr_t *attr, int sched_policy, int prio)
{
struct sched_param sched_param = {.sched_priority = prio};
int ret;
ret = pthread_attr_init(attr);
if (ret) {
fprintf(stderr, "pthread_attr_init(): %s\n", strerror(ret));
exit(PTS_UNRESOLVED);
}
ret = pthread_attr_setschedpolicy(attr, sched_policy);
if (ret) {
fprintf(stderr, "pthread_setschedpolicy(): %s\n", strerror(ret));
exit(PTS_UNRESOLVED);
}
ret = pthread_attr_setinheritsched(attr, PTHREAD_EXPLICIT_SCHED);
if (ret) {
fprintf(stderr, "pthread_attr_setinheritsched(): %s\n", strerror(ret));
exit(PTS_UNRESOLVED);
}
ret = pthread_attr_setschedparam(attr, &sched_param);
if (ret) {
fprintf(stderr, "pthread_attr_setschedparam(): %s\n", strerror(ret));
exit(PTS_UNRESOLVED);
}
}
static void run_hp_threads(int sched_policy, int sched_prio)
{
struct itimerval it;
pthread_t threads[n_threads];
pthread_attr_t attr;
int i, ret;
flag = 1;
it.it_interval.tv_sec = 0;
it.it_interval.tv_usec = 0;
it.it_value.tv_sec = n_threads / 20;
it.it_value.tv_usec = (n_threads % 20) * 50000;
init_attr(&attr, sched_policy, sched_prio);
if (signal(SIGPROF, alarm_handler) == SIG_ERR) {
perror("signal()");
exit(PTS_UNRESOLVED);
}
if (setitimer(ITIMER_PROF, &it, NULL)) {
perror("setitimer(ITIMER_VIRTUAL, ...)");
exit(PTS_UNRESOLVED);
}
for (i = 0; i < n_threads; i++) {
ret = pthread_create(&threads[i], &attr, do_work, NULL);
if (ret) {
fprintf(stderr, "pthread_create(): %s\n",
strerror(ret));
exit(PTS_UNRESOLVED);
}
}
if (flag) {
printf("FAILED: low priority thread scheduled\n");
exit(PTS_FAIL);
}
pthread_attr_destroy(&attr);
for (i = 0; i < n_threads; i++)
pthread_join(threads[i], NULL);
}
struct params {
int sched_policy;
int sched_priority;
};
static void *do_test(void *arg)
{
int ret, sched_policy;
struct sched_param param;
struct params *p = arg;
/* First check that the scheduler parameters are set correctly */
ret = pthread_getschedparam(pthread_self(), &sched_policy, &param);
if (ret) {
fprintf(stderr, "pthread_getschedparam(): %s\n", strerror(ret));
exit(PTS_UNRESOLVED);
}
if (p->sched_policy != sched_policy) {
printf("FAILED: have scheduler policy %i expected %i\n",
sched_policy, p->sched_policy);
exit(PTS_FAIL);
}
if (p->sched_priority != param.sched_priority) {
printf("FAILED: have scheduler priority %i expected %i\n",
p->sched_priority, param.sched_priority);
exit(PTS_FAIL);
}
/* Now check that priorities actually work */
run_hp_threads(p->sched_policy, p->sched_priority + 1);
return NULL;
}
struct tcase {
int sched_policy;
int prio;
};
enum tprio {
MIN,
HALF,
MAX_1,
};
struct tcase tcases[] = {
{SCHED_FIFO, MIN},
{SCHED_FIFO, HALF},
{SCHED_FIFO, MAX_1},
{SCHED_RR, MIN},
{SCHED_RR, HALF},
{SCHED_RR, MAX_1},
};
static int get_prio(struct tcase *self)
{
switch (self->prio) {
case MIN:
return sched_get_priority_min(self->sched_policy);
break;
case HALF:
return (sched_get_priority_min(self->sched_policy) +
sched_get_priority_max(self->sched_policy)) / 2;
break;
case MAX_1:
return sched_get_priority_max(self->sched_policy) - 1;
break;
}
printf("Wrong self->prio %i\n", self->prio);
exit(PTS_UNRESOLVED);
}
static const char *sched_policy_name(int policy)
{
switch (policy) {
case SCHED_FIFO:
return "SCHED_FIFO";
case SCHED_RR:
return "SCHED_RR";
default:
return "UNKNOWN";
}
}
int main(void)
{
pthread_attr_t attr;
pthread_t th;
struct params p;
int ret;
unsigned int i;
ret = set_affinity_single();
if (ret) {
n_threads = get_ncpu();
if (n_threads == -1) {
printf("Cannot get number of CPUs\n");
return PTS_UNRESOLVED;
}
printf("INFO: Affinity not supported, running %i threads.\n",
n_threads);
} else {
printf("INFO: Affinity works, will use only one thread.\n");
n_threads = 1;
}
for (i = 0; i < ARRAY_SIZE(tcases); i++) {
p.sched_policy = tcases[i].sched_policy;
p.sched_priority = get_prio(&tcases[i]);
init_attr(&attr, p.sched_policy, p.sched_priority);
printf("INFO: Testing %s prio %i\n",
sched_policy_name(p.sched_policy), p.sched_priority);
ret = pthread_create(&th, &attr, do_test, &p);
if (ret) {
fprintf(stderr, "pthread_create(): %s\n", strerror(ret));
return PTS_UNRESOLVED;
}
pthread_join(th, NULL);
pthread_attr_destroy(&attr);
}
printf("Test PASSED\n");
return 0;
}