Home | History | Annotate | Download | only in sched_setparam
      1 /*
      2  *  This program is free software; you can redistribute it and/or modify
      3  *  it under the terms of the GNU General Public License version 2.
      4  *
      5  *  This program is distributed in the hope that it will be useful,
      6  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      7  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      8  *  GNU General Public License for more details.
      9  *
     10  *
     11  * Test that the process specified by the pid argument preempt a lowest
     12  * priority running process, if the priority of the process specified by the
     13  * pid argument is set higher than that of the lowest priority running process
     14  * and if the specified process is ready to run.
     15  *
     16  * NO OTHER REALTIME PROCESS SHOULD RUN WHEN RUNNING THIS TEST.
     17  *
     18  * There is no portable way to get the number of CPUs but the test should work
     19  * for most of UNIX system (including but not limited to: Linux, Solaris, AIX,
     20  * HPUX, *BSD).
     21  * This test used shared memory.
     22  * Steps:
     23  *   1. Create a share memory segment.
     24  *   2. Change the policy to SCHED_FIFO and set minimum priority.
     25  *   3. Create NB_CPU-1 children processes which set their own priority to the
     26  *      higher value and use all but one processor.
     27  *   4. Create a child with the same priority.
     28  *   4. Call sched_setparam with an mean priority and the pid value of the
     29  *      last children.
     30  *   5. Check if the shared value has been changed by the child process. If
     31  *      not, the test fail.
     32  */
     33 #include "affinity.h"
     34 
     35 #include <errno.h>
     36 #include <sched.h>
     37 #include <stdio.h>
     38 #include <signal.h>
     39 #include <stdlib.h>
     40 #include <sys/ipc.h>
     41 #include <sys/shm.h>
     42 #include <time.h>
     43 #include <unistd.h>
     44 #include "posixtest.h"
     45 #include "ncpu.h"
     46 
     47 static int nb_cpu;
     48 static int *shmptr;
     49 static int mean_prio;
     50 
     51 static void child_process(void)
     52 {
     53 	struct sched_param param;
     54 	time_t t1, t2;
     55 
     56 	param.sched_priority = sched_get_priority_max(SCHED_FIFO);
     57 	if (sched_setparam(getpid(), &param) != 0) {
     58 		perror("An error occurs when calling sched_setparam()");
     59 		return;
     60 	}
     61 
     62 	t1 = time(NULL);
     63 	do {
     64 		t2 = time(NULL);
     65 	} while (difftime(t2, t1) <= 2);
     66 }
     67 
     68 static void test_process(void)
     69 {
     70 	struct sched_param param;
     71 	time_t t1, t2;
     72 
     73 	t1 = time(NULL);
     74 	do {
     75 		sched_getparam(getpid(), &param);
     76 		(*shmptr) = param.sched_priority;
     77 		/* if we can see that our priority has changed
     78 		 * that means we preempted parent, so we are done */
     79 		if ((*shmptr) == mean_prio)
     80 			break;
     81 
     82 		t2 = time(NULL);
     83 		/* OS-es supporting set_affinity(), like Linux, will
     84 		 * have only one parent and child process competing
     85 		 * for same CPU. Since code path in parent does not
     86 		 * block between fork() and sched_setparam(), child
     87 		 * should run only after parent boosted its priority.
     88 		 * Situation on other OS-es is a bit less predictable,
     89 		 * as these will spawn ncpu-1 children, which run at max
     90 		 * priority and could (though unlikely) preempt parent.
     91 		 * Rather than risking deadlock, keep the sched_yield()
     92 		 * call in loop as it is harmless. */
     93 		sched_yield();
     94 	} while (difftime(t2, t1) <= 2);
     95 	pause();
     96 }
     97 
     98 static void kill_children(int *child_pid, int count)
     99 {
    100 	int i;
    101 
    102 	for (i = 0; i < count; i++)
    103 		kill(child_pid[i], SIGTERM);
    104 	free(child_pid);
    105 }
    106 
    107 int main(void)
    108 {
    109 	int *child_pid, oldcount, newcount, shm_id, i;
    110 	struct sched_param param;
    111 	key_t key;
    112 	int rc = set_affinity_single();
    113 	if (rc) {
    114 		nb_cpu = get_ncpu();
    115 		if (nb_cpu == -1) {
    116 			printf("Can not get the number of"
    117 			       " CPUs of the machine.\n");
    118 			return PTS_UNRESOLVED;
    119 		}
    120 	} else {
    121 		nb_cpu = 1;
    122 	}
    123 
    124 	mean_prio = (sched_get_priority_min(SCHED_FIFO) +
    125 		sched_get_priority_max(SCHED_FIFO)) / 2;
    126 	child_pid = malloc(nb_cpu * sizeof(int));
    127 
    128 	key = ftok("conformance/interfaces/sched_setparam/9-1.c", 1234);
    129 	shm_id = shmget(key, sizeof(int), IPC_CREAT | 0600);
    130 	if (shm_id < 0) {
    131 		perror("An error occurs when calling shmget()");
    132 		return PTS_UNRESOLVED;
    133 	}
    134 
    135 	shmptr = shmat(shm_id, 0, 0);
    136 	if (shmptr == (void *)-1) {
    137 		perror("An error occurs when calling shmat()");
    138 		return PTS_UNRESOLVED;
    139 	}
    140 	*shmptr = 0;
    141 
    142 	param.sched_priority = sched_get_priority_min(SCHED_FIFO);
    143 	if (sched_setscheduler(getpid(), SCHED_FIFO, &param) == -1) {
    144 		if (errno == EPERM) {
    145 			printf("This process does not have the permission"
    146 			       " to set its own scheduling parameter.\n"
    147 			       "Try to launch this test as root\n");
    148 		} else {
    149 			perror("An error occurs when calling"
    150 			       " sched_setscheduler()");
    151 		}
    152 		return PTS_UNRESOLVED;
    153 	}
    154 
    155 	for (i = 0; i < (nb_cpu - 1); i++) {
    156 		child_pid[i] = fork();
    157 		if (child_pid[i] == -1) {
    158 			perror("An error occurs when calling fork()");
    159 			kill_children(child_pid, i);
    160 			return PTS_UNRESOLVED;
    161 		} else if (child_pid[i] == 0) {
    162 
    163 			child_process();
    164 
    165 			printf("This code should not be executed.\n");
    166 			return PTS_UNRESOLVED;
    167 		}
    168 	}
    169 
    170 	child_pid[i] = fork();
    171 	if (child_pid[i] == -1) {
    172 		perror("An error occurs when calling fork()");
    173 		kill_children(child_pid, i);
    174 		return PTS_UNRESOLVED;
    175 	} else if (child_pid[i] == 0) {
    176 
    177 		test_process();
    178 
    179 		printf("This code should not be executed.\n");
    180 		return PTS_UNRESOLVED;
    181 	}
    182 
    183 	param.sched_priority = mean_prio;
    184 	oldcount = *shmptr;
    185 	if (sched_setparam(child_pid[i], &param) != 0) {
    186 		perror("An error occurs when calling sched_setparam()");
    187 		kill_children(child_pid, nb_cpu);
    188 		return PTS_UNRESOLVED;
    189 	}
    190 	newcount = *shmptr;
    191 
    192 	if (newcount == oldcount) {
    193 		printf("The target process does not preempt"
    194 		       " the calling process\n");
    195 		kill_children(child_pid, nb_cpu);
    196 		return PTS_FAIL;
    197 	}
    198 
    199 	printf("Test PASSED\n");
    200 	kill_children(child_pid, nb_cpu);
    201 	return PTS_PASS;
    202 }
    203