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
child_process(void)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(), ¶m) != 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
test_process(void)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(), ¶m);
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
kill_children(int * child_pid,int count)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
main(void)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, ¶m) != 0) {
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], ¶m) != 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