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 self->ul_rtclassid = pccp->pcc_info.pc_cid; 613 priority = ((rtparms_t *)pcparm.pc_clparms)->rt_pri; 614 policy = 615 ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs == RT_TQINF? 616 SCHED_FIFO : SCHED_RR; 617 break; 618 default: 619 /* 620 * Class-independent method for getting the priority. 621 */ 622 pcprio.pc_op = PC_GETPRIO; 623 pcprio.pc_cid = 0; 624 pcprio.pc_val = 0; 625 if (_private_priocntl(P_LWPID, P_MYID, PC_DOPRIO, &pcprio) == 0) 626 priority = pcprio.pc_val; 627 else 628 priority = 0; 629 } 630 631 self->ul_cid = pcparm.pc_cid; 632 self->ul_pri = priority; 633 _membar_producer(); 634 self->ul_policy = policy; 635 636 exit_critical(self); 637 } 638