xref: /illumos-gate/usr/src/lib/libc/port/rt/sched.c (revision 7257d1b4)
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