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