Home | History | Annotate | Download | only in rt
      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
     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 (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 *
    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 *
    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
    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
    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
    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
    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
    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
    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
    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
    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
    479 sched_yield(void)
    480 {
    481 	yield();
    482 	return (0);
    483 }
    484 
    485 int
    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
    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
    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
    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