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