1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 #include "lint.h"
30 #include "thr_uberdata.h"
31 #include <sched.h>
32 #include <sys/tspriocntl.h>
33 #include <sys/rtpriocntl.h>
34 #include <sys/fxpriocntl.h>
35
36 /*
37 * The following array is used for caching information
38 * for priocntl scheduling classes.
39 */
40 static pcclass_t sched_class[] = {
41 {0, SCHED_OTHER, 0, 0, {-1, "TS", 0}},
42 {0, SCHED_FIFO, 0, 0, {-1, "RT", 0}},
43 {0, SCHED_RR, 0, 0, {-1, "RT", 0}},
44 {0, SCHED_SYS, 0, 0, {0, "SYS", 0}},
45 {0, SCHED_IA, 0, 0, {-1, "IA", 0}},
46 {0, SCHED_FSS, 0, 0, {-1, "FSS", 0}},
47 {0, SCHED_FX, 0, 0, {-1, "FX", 0}},
48 /*
49 * Allow unknown (to us) scheduling classes.
50 * The kernel allows space for exactly 10 scheduling classes
51 * (see the definitions of 'sclass' and 'nclass' in the kernel).
52 * We need that number of available slots here.
53 * If the kernel space is changed, this has to change too.
54 */
55 {0, -1, 0, 0, {-1, "", 0}},
56 {0, -1, 0, 0, {-1, "", 0}},
57 {0, -1, 0, 0, {-1, "", 0}},
58 {0, -1, 0, 0, {-1, "", 0}},
59 {0, -1, 0, 0, {-1, "", 0}},
60 {0, -1, 0, 0, {-1, "", 0}},
61 {0, -1, 0, 0, {-1, "", 0}},
62 {0, -1, 0, 0, {-1, "", 0}},
63 {0, -1, 0, 0, {-1, "", 0}},
64 {0, -1, 0, 0, {-1, "", 0}},
65 };
66
67 #define NPOLICY (sizeof (sched_class) / sizeof (pcclass_t))
68
69 #if _SCHED_NEXT != SCHED_FX + 1
70 #error "fatal: _SCHED_NEXT != SCHED_FX + 1"
71 #endif
72
73 static mutex_t class_lock = DEFAULTMUTEX; /* protects sched_class[] */
74
75 /*
76 * Helper function for get_info_by_policy(), below.
77 * Don't let a manufactured policy number duplicate
78 * the class of one of our base policy numbers.
79 */
80 static int
is_base_class(const char * clname)81 is_base_class(const char *clname)
82 {
83 const pcclass_t *pccp;
84 int policy;
85
86 for (policy = 0, pccp = sched_class;
87 policy < _SCHED_NEXT;
88 policy++, pccp++) {
89 if (strcmp(clname, pccp->pcc_info.pc_clname) == 0)
90 return (1);
91 }
92 return (0);
93 }
94
95 /*
96 * Cache priocntl information on scheduling class by policy.
97 */
98 const pcclass_t *
get_info_by_policy(int policy)99 get_info_by_policy(int policy)
100 {
101 pcclass_t *pccp = &sched_class[policy];
102 pcpri_t pcpri;
103 pri_t prio;
104 int base = 0;
105
106 if ((uint_t)policy >= NPOLICY || pccp->pcc_state < 0) {
107 errno = EINVAL;
108 return (NULL);
109 }
110
111 if (pccp->pcc_state > 0)
112 return (pccp);
113
114 lmutex_lock(&class_lock);
115
116 /* get class info (the system class is known to have class-id == 0) */
117 if (pccp->pcc_policy == -1) {
118 /* policy number not defined in <sched.h> */
119 ASSERT(policy >= _SCHED_NEXT);
120 pccp->pcc_info.pc_cid = policy - _SCHED_NEXT;
121 if (priocntl(0, 0, PC_GETCLINFO, &pccp->pcc_info) == -1 ||
122 (base = is_base_class(pccp->pcc_info.pc_clname)) != 0) {
123 pccp->pcc_info.pc_clname[0] = '\0';
124 pccp->pcc_info.pc_cid = -1;
125 /*
126 * If we duplicated a base class, permanently
127 * disable this policy entry. Else allow for
128 * dynamic loading of scheduling classes.
129 */
130 if (base) {
131 membar_producer();
132 pccp->pcc_state = -1;
133 }
134 errno = EINVAL;
135 lmutex_unlock(&class_lock);
136 return (NULL);
137 }
138 pccp->pcc_policy = policy;
139 } else if (policy != SCHED_SYS &&
140 priocntl(0, 0, PC_GETCID, &pccp->pcc_info) == -1) {
141 membar_producer();
142 pccp->pcc_state = -1;
143 errno = EINVAL;
144 lmutex_unlock(&class_lock);
145 return (NULL);
146 }
147
148 switch (policy) {
149 case SCHED_OTHER:
150 prio = ((tsinfo_t *)pccp->pcc_info.pc_clinfo)->ts_maxupri;
151 pccp->pcc_primin = -prio;
152 pccp->pcc_primax = prio;
153 break;
154 case SCHED_FIFO:
155 case SCHED_RR:
156 prio = ((rtinfo_t *)pccp->pcc_info.pc_clinfo)->rt_maxpri;
157 pccp->pcc_primin = 0;
158 pccp->pcc_primax = prio;
159 break;
160 default:
161 /*
162 * All other policy numbers, including policy numbers
163 * not defined in <sched.h>.
164 */
165 pcpri.pc_cid = pccp->pcc_info.pc_cid;
166 if (priocntl(0, 0, PC_GETPRIRANGE, &pcpri) == 0) {
167 pccp->pcc_primin = pcpri.pc_clpmin;
168 pccp->pcc_primax = pcpri.pc_clpmax;
169 }
170 break;
171 }
172
173 membar_producer();
174 pccp->pcc_state = 1;
175 lmutex_unlock(&class_lock);
176 return (pccp);
177 }
178
179 const pcclass_t *
get_info_by_class(id_t classid)180 get_info_by_class(id_t classid)
181 {
182 pcinfo_t pcinfo;
183 pcclass_t *pccp;
184 int policy;
185
186 if (classid < 0) {
187 errno = EINVAL;
188 return (NULL);
189 }
190
191 /* determine if we already know this classid */
192 for (policy = 0, pccp = sched_class;
193 policy < NPOLICY;
194 policy++, pccp++) {
195 if (pccp->pcc_state > 0 && pccp->pcc_info.pc_cid == classid)
196 return (pccp);
197 }
198
199 pcinfo.pc_cid = classid;
200 if (priocntl(0, 0, PC_GETCLINFO, &pcinfo) == -1) {
201 if (classid == 0) /* no kernel info for sys class */
202 return (get_info_by_policy(SCHED_SYS));
203 return (NULL);
204 }
205
206 for (policy = 0, pccp = sched_class;
207 policy < NPOLICY;
208 policy++, pccp++) {
209 if (pccp->pcc_state == 0 &&
210 strcmp(pcinfo.pc_clname, pccp->pcc_info.pc_clname) == 0)
211 return (get_info_by_policy(pccp->pcc_policy));
212 }
213
214 /*
215 * We have encountered an unknown (to us) scheduling class.
216 * Manufacture a policy number for it. Hopefully we still
217 * have room in the sched_class[] table.
218 */
219 policy = _SCHED_NEXT + classid;
220 if (policy >= NPOLICY) {
221 errno = EINVAL;
222 return (NULL);
223 }
224 lmutex_lock(&class_lock);
225 pccp = &sched_class[policy];
226 pccp->pcc_policy = policy;
227 (void) strlcpy(pccp->pcc_info.pc_clname, pcinfo.pc_clname, PC_CLNMSZ);
228 lmutex_unlock(&class_lock);
229 return (get_info_by_policy(pccp->pcc_policy));
230 }
231
232 /*
233 * Helper function: get process or lwp current scheduling policy.
234 */
235 static const pcclass_t *
get_parms(idtype_t idtype,id_t id,pcparms_t * pcparmp)236 get_parms(idtype_t idtype, id_t id, pcparms_t *pcparmp)
237 {
238 pcparmp->pc_cid = PC_CLNULL;
239 if (priocntl(idtype, id, PC_GETPARMS, pcparmp) == -1)
240 return (NULL);
241 return (get_info_by_class(pcparmp->pc_cid));
242 }
243
244 /*
245 * Helper function for setprio() and setparam(), below.
246 */
247 static int
set_priority(idtype_t idtype,id_t id,int policy,int prio,pcparms_t * pcparmp,int settq)248 set_priority(idtype_t idtype, id_t id, int policy, int prio,
249 pcparms_t *pcparmp, int settq)
250 {
251 int rv;
252
253 switch (policy) {
254 case SCHED_OTHER:
255 {
256 tsparms_t *tsp = (tsparms_t *)pcparmp->pc_clparms;
257 tsp->ts_uprilim = prio;
258 tsp->ts_upri = prio;
259 break;
260 }
261 case SCHED_FIFO:
262 case SCHED_RR:
263 {
264 rtparms_t *rtp = (rtparms_t *)pcparmp->pc_clparms;
265 rtp->rt_tqnsecs = settq?
266 (policy == SCHED_FIFO? RT_TQINF : RT_TQDEF) :
267 RT_NOCHANGE;
268 rtp->rt_pri = prio;
269 break;
270 }
271 default:
272 {
273 /*
274 * Class-independent method for setting the priority.
275 */
276 pcprio_t pcprio;
277
278 pcprio.pc_op = PC_SETPRIO;
279 pcprio.pc_cid = pcparmp->pc_cid;
280 pcprio.pc_val = prio;
281 do {
282 rv = priocntl(idtype, id, PC_DOPRIO, &pcprio);
283 } while (rv == -1 && errno == ENOMEM);
284 return (rv);
285 }
286 }
287
288 do {
289 rv = priocntl(idtype, id, PC_SETPARMS, pcparmp);
290 } while (rv == -1 && errno == ENOMEM);
291 return (rv);
292 }
293
294 /*
295 * Utility function, private to libc, used by sched_setparam()
296 * and posix_spawn(). Because it is called by the vfork() child of
297 * posix_spawn(), we must not call any functions exported from libc.
298 */
299 id_t
setprio(idtype_t idtype,id_t id,int prio,int * policyp)300 setprio(idtype_t idtype, id_t id, int prio, int *policyp)
301 {
302 pcparms_t pcparm;
303 int policy;
304 const pcclass_t *pccp;
305
306 if ((pccp = get_parms(idtype, id, &pcparm)) == NULL)
307 return (-1);
308 if (prio < pccp->pcc_primin || prio > pccp->pcc_primax) {
309 errno = EINVAL;
310 return (-1);
311 }
312
313 policy = pccp->pcc_policy;
314 if (policyp != NULL &&
315 (policy == SCHED_FIFO || policy == SCHED_RR)) {
316 rtparms_t *rtp = (rtparms_t *)pcparm.pc_clparms;
317 policy = (rtp->rt_tqnsecs == RT_TQINF? SCHED_FIFO : SCHED_RR);
318 }
319
320 if (set_priority(idtype, id, policy, prio, &pcparm, 0) == -1)
321 return (-1);
322 if (policyp != NULL)
323 *policyp = policy;
324 return (pccp->pcc_info.pc_cid);
325 }
326
327 int
sched_setparam(pid_t pid,const struct sched_param * param)328 sched_setparam(pid_t pid, const struct sched_param *param)
329 {
330 if (pid < 0) {
331 errno = ESRCH;
332 return (-1);
333 }
334 if (pid == 0)
335 pid = P_MYID;
336
337 if (setprio(P_PID, pid, param->sched_priority, NULL) == -1)
338 return (-1);
339 return (0);
340 }
341
342 id_t
getparam(idtype_t idtype,id_t id,int * policyp,struct sched_param * param)343 getparam(idtype_t idtype, id_t id, int *policyp, struct sched_param *param)
344 {
345 pcparms_t pcparm;
346 const pcclass_t *pccp;
347 int policy;
348 int priority;
349
350 if ((pccp = get_parms(idtype, id, &pcparm)) == NULL)
351 return (-1);
352
353 switch (policy = pccp->pcc_policy) {
354 case SCHED_OTHER:
355 {
356 tsparms_t *tsp = (tsparms_t *)pcparm.pc_clparms;
357 priority = tsp->ts_upri;
358 break;
359 }
360 case SCHED_FIFO:
361 case SCHED_RR:
362 {
363 rtparms_t *rtp = (rtparms_t *)pcparm.pc_clparms;
364 priority = rtp->rt_pri;
365 policy = (rtp->rt_tqnsecs == RT_TQINF? SCHED_FIFO : SCHED_RR);
366 break;
367 }
368 default:
369 {
370 /*
371 * Class-independent method for getting the priority.
372 */
373 pcprio_t pcprio;
374
375 pcprio.pc_op = PC_GETPRIO;
376 pcprio.pc_cid = 0;
377 pcprio.pc_val = 0;
378 if (priocntl(idtype, id, PC_DOPRIO, &pcprio) == 0)
379 priority = pcprio.pc_val;
380 else
381 priority = 0;
382 break;
383 }
384 }
385
386 *policyp = policy;
387 (void) memset(param, 0, sizeof (*param));
388 param->sched_priority = priority;
389
390 return (pcparm.pc_cid);
391 }
392
393 int
sched_getparam(pid_t pid,struct sched_param * param)394 sched_getparam(pid_t pid, struct sched_param *param)
395 {
396 int policy;
397
398 if (pid < 0) {
399 errno = ESRCH;
400 return (-1);
401 }
402 if (pid == 0)
403 pid = P_MYID;
404
405 if (getparam(P_PID, pid, &policy, param) == -1)
406 return (-1);
407 return (0);
408 }
409
410 /*
411 * Utility function, private to libc, used by sched_setscheduler()
412 * and posix_spawn(). Because it is called by the vfork() child of
413 * posix_spawn(), we must not call any functions exported from libc.
414 */
415 id_t
setparam(idtype_t idtype,id_t id,int policy,int prio)416 setparam(idtype_t idtype, id_t id, int policy, int prio)
417 {
418 pcparms_t pcparm;
419 const pcclass_t *pccp;
420
421 if (policy == SCHED_SYS ||
422 (pccp = get_info_by_policy(policy)) == NULL ||
423 prio < pccp->pcc_primin || prio > pccp->pcc_primax) {
424 errno = EINVAL;
425 return (-1);
426 }
427
428 pcparm.pc_cid = pccp->pcc_info.pc_cid;
429 if (set_priority(idtype, id, policy, prio, &pcparm, 1) == -1)
430 return (-1);
431 return (pccp->pcc_info.pc_cid);
432 }
433
434 int
sched_setscheduler(pid_t pid,int policy,const struct sched_param * param)435 sched_setscheduler(pid_t pid, int policy, const struct sched_param *param)
436 {
437 pri_t prio = param->sched_priority;
438 int oldpolicy;
439
440 if ((oldpolicy = sched_getscheduler(pid)) < 0)
441 return (-1);
442
443 if (pid == 0)
444 pid = P_MYID;
445
446 if (setparam(P_PID, pid, policy, prio) == -1)
447 return (-1);
448
449 return (oldpolicy);
450 }
451
452 int
sched_getscheduler(pid_t pid)453 sched_getscheduler(pid_t pid)
454 {
455 pcparms_t pcparm;
456 const pcclass_t *pccp;
457 int policy;
458
459 if (pid < 0) {
460 errno = ESRCH;
461 return (-1);
462 }
463 if (pid == 0)
464 pid = P_MYID;
465
466 if ((pccp = get_parms(P_PID, pid, &pcparm)) == NULL)
467 return (-1);
468
469 if ((policy = pccp->pcc_policy) == SCHED_FIFO || policy == SCHED_RR) {
470 policy =
471 (((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs == RT_TQINF?
472 SCHED_FIFO : SCHED_RR);
473 }
474
475 return (policy);
476 }
477
478 int
sched_yield(void)479 sched_yield(void)
480 {
481 yield();
482 return (0);
483 }
484
485 int
sched_get_priority_max(int policy)486 sched_get_priority_max(int policy)
487 {
488 const pcclass_t *pccp;
489
490 if ((pccp = get_info_by_policy(policy)) != NULL)
491 return (pccp->pcc_primax);
492 errno = EINVAL;
493 return (-1);
494 }
495
496 int
sched_get_priority_min(int policy)497 sched_get_priority_min(int policy)
498 {
499 const pcclass_t *pccp;
500
501 if ((pccp = get_info_by_policy(policy)) != NULL)
502 return (pccp->pcc_primin);
503 errno = EINVAL;
504 return (-1);
505 }
506
507 int
sched_rr_get_interval(pid_t pid,timespec_t * interval)508 sched_rr_get_interval(pid_t pid, timespec_t *interval)
509 {
510 pcparms_t pcparm;
511 const pcclass_t *pccp;
512
513 if (pid < 0) {
514 errno = ESRCH;
515 return (-1);
516 }
517 if (pid == 0)
518 pid = P_MYID;
519
520 if ((pccp = get_parms(P_PID, pid, &pcparm)) == NULL)
521 return (-1);
522
523 /*
524 * At the moment, we have no class-independent method to fetch
525 * the process/lwp time quantum. Since SUSv3 does not restrict
526 * this operation to the real-time class, we return an indefinite
527 * quantum (tv_sec == 0 and tv_nsec == 0) for scheduling policies
528 * for which this information isn't available.
529 */
530 interval->tv_sec = 0;
531 interval->tv_nsec = 0;
532
533 switch (pccp->pcc_policy) {
534 case SCHED_FIFO:
535 case SCHED_RR:
536 {
537 rtparms_t *rtp = (rtparms_t *)pcparm.pc_clparms;
538 if (rtp->rt_tqnsecs != RT_TQINF) {
539 interval->tv_sec = rtp->rt_tqsecs;
540 interval->tv_nsec = rtp->rt_tqnsecs;
541 }
542 }
543 break;
544 case SCHED_FX:
545 {
546 fxparms_t *fxp = (fxparms_t *)pcparm.pc_clparms;
547 if (fxp->fx_tqnsecs != FX_TQINF) {
548 interval->tv_sec = fxp->fx_tqsecs;
549 interval->tv_nsec = fxp->fx_tqnsecs;
550 }
551 }
552 break;
553 }
554
555 return (0);
556 }
557
558 /*
559 * Initialize or update ul_policy, ul_cid, and ul_pri.
560 */
561 void
update_sched(ulwp_t * self)562 update_sched(ulwp_t *self)
563 {
564 volatile sc_shared_t *scp;
565 pcparms_t pcparm;
566 pcprio_t pcprio;
567 const pcclass_t *pccp;
568 int priority;
569 int policy;
570
571 ASSERT(self == curthread);
572
573 enter_critical(self);
574
575 if ((scp = self->ul_schedctl) == NULL &&
576 (scp = setup_schedctl()) == NULL) { /* can't happen? */
577 if (self->ul_policy < 0) {
578 self->ul_cid = 0;
579 self->ul_pri = 0;
580 membar_producer();
581 self->ul_policy = SCHED_OTHER;
582 }
583 exit_critical(self);
584 return;
585 }
586
587 if (self->ul_policy >= 0 &&
588 self->ul_cid == scp->sc_cid &&
589 (self->ul_pri == scp->sc_cpri ||
590 (self->ul_epri > 0 && self->ul_epri == scp->sc_cpri))) {
591 exit_critical(self);
592 return;
593 }
594
595 pccp = get_parms(P_LWPID, P_MYID, &pcparm);
596 if (pccp == NULL) { /* can't happen? */
597 self->ul_cid = scp->sc_cid;
598 self->ul_pri = scp->sc_cpri;
599 membar_producer();
600 self->ul_policy = SCHED_OTHER;
601 exit_critical(self);
602 return;
603 }
604
605 switch (policy = pccp->pcc_policy) {
606 case SCHED_OTHER:
607 priority = ((tsparms_t *)pcparm.pc_clparms)->ts_upri;
608 break;
609 case SCHED_FIFO:
610 case SCHED_RR:
611 self->ul_rtclassid = pccp->pcc_info.pc_cid;
612 priority = ((rtparms_t *)pcparm.pc_clparms)->rt_pri;
613 policy =
614 ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs == RT_TQINF?
615 SCHED_FIFO : SCHED_RR;
616 break;
617 default:
618 /*
619 * Class-independent method for getting the priority.
620 */
621 pcprio.pc_op = PC_GETPRIO;
622 pcprio.pc_cid = 0;
623 pcprio.pc_val = 0;
624 if (priocntl(P_LWPID, P_MYID, PC_DOPRIO, &pcprio) == 0)
625 priority = pcprio.pc_val;
626 else
627 priority = 0;
628 }
629
630 self->ul_cid = pcparm.pc_cid;
631 self->ul_pri = priority;
632 membar_producer();
633 self->ul_policy = policy;
634
635 exit_critical(self);
636 }
637