| /* |
| * 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 |
| */ |
| |
| /* We are testing conformance to IEEE Std 1003.1, 2003 Edition */ |
| #define _POSIX_C_SOURCE 200112L |
| |
| /* 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, ¶m); |
| 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; |
| } |