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