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.
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.
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]
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
30 #include "thr_uberdata.h"
32 #include <sys/tspriocntl.h>
33 #include <sys/rtpriocntl.h>
34 #include <sys/fxpriocntl.h>
37 * The following array is used for caching information
38 * for priocntl scheduling classes.
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}},
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.
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}},
67 #define NPOLICY (sizeof (sched_class) / sizeof (pcclass_t))
69 #if _SCHED_NEXT != SCHED_FX + 1
70 #error "fatal: _SCHED_NEXT != SCHED_FX + 1"
73 static mutex_t class_lock
= DEFAULTMUTEX
; /* protects sched_class[] */
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.
81 is_base_class(const char *clname
)
83 const pcclass_t
*pccp
;
86 for (policy
= 0, pccp
= sched_class
;
89 if (strcmp(clname
, pccp
->pcc_info
.pc_clname
) == 0)
96 * Cache priocntl information on scheduling class by policy.
99 get_info_by_policy(int policy
)
101 pcclass_t
*pccp
= &sched_class
[policy
];
106 if ((uint_t
)policy
>= NPOLICY
|| pccp
->pcc_state
< 0) {
111 if (pccp
->pcc_state
> 0)
114 lmutex_lock(&class_lock
);
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;
126 * If we duplicated a base class, permanently
127 * disable this policy entry. Else allow for
128 * dynamic loading of scheduling classes.
132 pccp
->pcc_state
= -1;
135 lmutex_unlock(&class_lock
);
138 pccp
->pcc_policy
= policy
;
139 } else if (policy
!= SCHED_SYS
&&
140 priocntl(0, 0, PC_GETCID
, &pccp
->pcc_info
) == -1) {
142 pccp
->pcc_state
= -1;
144 lmutex_unlock(&class_lock
);
150 prio
= ((tsinfo_t
*)pccp
->pcc_info
.pc_clinfo
)->ts_maxupri
;
151 pccp
->pcc_primin
= -prio
;
152 pccp
->pcc_primax
= prio
;
156 prio
= ((rtinfo_t
*)pccp
->pcc_info
.pc_clinfo
)->rt_maxpri
;
157 pccp
->pcc_primin
= 0;
158 pccp
->pcc_primax
= prio
;
162 * All other policy numbers, including policy numbers
163 * not defined in <sched.h>.
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
;
175 lmutex_unlock(&class_lock
);
180 get_info_by_class(id_t classid
)
191 /* determine if we already know this classid */
192 for (policy
= 0, pccp
= sched_class
;
195 if (pccp
->pcc_state
> 0 && pccp
->pcc_info
.pc_cid
== classid
)
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
));
206 for (policy
= 0, pccp
= sched_class
;
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
));
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.
219 policy
= _SCHED_NEXT
+ classid
;
220 if (policy
>= NPOLICY
) {
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
));
233 * Helper function: get process or lwp current scheduling policy.
235 static const pcclass_t
*
236 get_parms(idtype_t idtype
, id_t id
, pcparms_t
*pcparmp
)
238 pcparmp
->pc_cid
= PC_CLNULL
;
239 if (priocntl(idtype
, id
, PC_GETPARMS
, pcparmp
) == -1)
241 return (get_info_by_class(pcparmp
->pc_cid
));
245 * Helper function for setprio() and setparam(), below.
248 set_priority(idtype_t idtype
, id_t id
, int policy
, int prio
,
249 pcparms_t
*pcparmp
, int settq
)
256 tsparms_t
*tsp
= (tsparms_t
*)pcparmp
->pc_clparms
;
257 tsp
->ts_uprilim
= prio
;
264 rtparms_t
*rtp
= (rtparms_t
*)pcparmp
->pc_clparms
;
265 rtp
->rt_tqnsecs
= settq
?
266 (policy
== SCHED_FIFO
? RT_TQINF
: RT_TQDEF
) :
274 * Class-independent method for setting the priority.
278 pcprio
.pc_op
= PC_SETPRIO
;
279 pcprio
.pc_cid
= pcparmp
->pc_cid
;
280 pcprio
.pc_val
= prio
;
282 rv
= priocntl(idtype
, id
, PC_DOPRIO
, &pcprio
);
283 } while (rv
== -1 && errno
== ENOMEM
);
289 rv
= priocntl(idtype
, id
, PC_SETPARMS
, pcparmp
);
290 } while (rv
== -1 && errno
== ENOMEM
);
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.
300 setprio(idtype_t idtype
, id_t id
, int prio
, int *policyp
)
304 const pcclass_t
*pccp
;
306 if ((pccp
= get_parms(idtype
, id
, &pcparm
)) == NULL
)
308 if (prio
< pccp
->pcc_primin
|| prio
> pccp
->pcc_primax
) {
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
);
320 if (set_priority(idtype
, id
, policy
, prio
, &pcparm
, 0) == -1)
324 return (pccp
->pcc_info
.pc_cid
);
328 sched_setparam(pid_t pid
, const struct sched_param
*param
)
337 if (setprio(P_PID
, pid
, param
->sched_priority
, NULL
) == -1)
343 getparam(idtype_t idtype
, id_t id
, int *policyp
, struct sched_param
*param
)
346 const pcclass_t
*pccp
;
350 if ((pccp
= get_parms(idtype
, id
, &pcparm
)) == NULL
)
353 switch (policy
= pccp
->pcc_policy
) {
356 tsparms_t
*tsp
= (tsparms_t
*)pcparm
.pc_clparms
;
357 priority
= tsp
->ts_upri
;
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
);
371 * Class-independent method for getting the priority.
375 pcprio
.pc_op
= PC_GETPRIO
;
378 if (priocntl(idtype
, id
, PC_DOPRIO
, &pcprio
) == 0)
379 priority
= pcprio
.pc_val
;
387 (void) memset(param
, 0, sizeof (*param
));
388 param
->sched_priority
= priority
;
390 return (pcparm
.pc_cid
);
394 sched_getparam(pid_t pid
, struct sched_param
*param
)
405 if (getparam(P_PID
, pid
, &policy
, param
) == -1)
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.
416 setparam(idtype_t idtype
, id_t id
, int policy
, int prio
)
419 const pcclass_t
*pccp
;
421 if (policy
== SCHED_SYS
||
422 (pccp
= get_info_by_policy(policy
)) == NULL
||
423 prio
< pccp
->pcc_primin
|| prio
> pccp
->pcc_primax
) {
428 pcparm
.pc_cid
= pccp
->pcc_info
.pc_cid
;
429 if (set_priority(idtype
, id
, policy
, prio
, &pcparm
, 1) == -1)
431 return (pccp
->pcc_info
.pc_cid
);
435 sched_setscheduler(pid_t pid
, int policy
, const struct sched_param
*param
)
437 pri_t prio
= param
->sched_priority
;
440 if ((oldpolicy
= sched_getscheduler(pid
)) < 0)
446 if (setparam(P_PID
, pid
, policy
, prio
) == -1)
453 sched_getscheduler(pid_t pid
)
456 const pcclass_t
*pccp
;
466 if ((pccp
= get_parms(P_PID
, pid
, &pcparm
)) == NULL
)
469 if ((policy
= pccp
->pcc_policy
) == SCHED_FIFO
|| policy
== SCHED_RR
) {
471 (((rtparms_t
*)pcparm
.pc_clparms
)->rt_tqnsecs
== RT_TQINF
?
472 SCHED_FIFO
: SCHED_RR
);
486 sched_get_priority_max(int policy
)
488 const pcclass_t
*pccp
;
490 if ((pccp
= get_info_by_policy(policy
)) != NULL
)
491 return (pccp
->pcc_primax
);
497 sched_get_priority_min(int policy
)
499 const pcclass_t
*pccp
;
501 if ((pccp
= get_info_by_policy(policy
)) != NULL
)
502 return (pccp
->pcc_primin
);
508 sched_rr_get_interval(pid_t pid
, timespec_t
*interval
)
511 const pcclass_t
*pccp
;
520 if ((pccp
= get_parms(P_PID
, pid
, &pcparm
)) == NULL
)
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.
530 interval
->tv_sec
= 0;
531 interval
->tv_nsec
= 0;
533 switch (pccp
->pcc_policy
) {
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
;
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
;
559 * Initialize or update ul_policy, ul_cid, and ul_pri.
562 update_sched(ulwp_t
*self
)
564 volatile sc_shared_t
*scp
;
567 const pcclass_t
*pccp
;
571 ASSERT(self
== curthread
);
573 enter_critical(self
);
575 if ((scp
= self
->ul_schedctl
) == NULL
&&
576 (scp
= setup_schedctl()) == NULL
) { /* can't happen? */
577 if (self
->ul_policy
< 0) {
581 self
->ul_policy
= SCHED_OTHER
;
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
))) {
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
;
600 self
->ul_policy
= SCHED_OTHER
;
605 switch (policy
= pccp
->pcc_policy
) {
607 priority
= ((tsparms_t
*)pcparm
.pc_clparms
)->ts_upri
;
611 self
->ul_rtclassid
= pccp
->pcc_info
.pc_cid
;
612 priority
= ((rtparms_t
*)pcparm
.pc_clparms
)->rt_pri
;
614 ((rtparms_t
*)pcparm
.pc_clparms
)->rt_tqnsecs
== RT_TQINF
?
615 SCHED_FIFO
: SCHED_RR
;
619 * Class-independent method for getting the priority.
621 pcprio
.pc_op
= PC_GETPRIO
;
624 if (priocntl(P_LWPID
, P_MYID
, PC_DOPRIO
, &pcprio
) == 0)
625 priority
= pcprio
.pc_val
;
630 self
->ul_cid
= pcparm
.pc_cid
;
631 self
->ul_pri
= priority
;
633 self
->ul_policy
= policy
;