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