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 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 * 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 * 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 * 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 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 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 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 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 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 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 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 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 477 sched_yield(void) 478 { 479 yield(); 480 return (0); 481 } 482 483 int 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 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 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 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