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 2006 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 "mtlib.h" 31 #include <sys/types.h> 32 #include <sched.h> 33 #include <errno.h> 34 #include <limits.h> 35 #include <unistd.h> 36 #include <sys/priocntl.h> 37 #include <sys/rtpriocntl.h> 38 #include <sys/tspriocntl.h> 39 #include <sys/rt.h> 40 #include <sys/ts.h> 41 #include <thread.h> 42 #include <string.h> 43 #include <stdlib.h> 44 #include "rtsched.h" 45 46 /* 47 * The following variables are used for caching information 48 * for priocntl scheduling classes. 49 */ 50 struct pcclass ts_class; 51 struct pcclass rt_class; 52 struct pcclass ia_class; 53 struct pcclass sys_class; 54 55 static rtdpent_t *rt_dptbl; /* RT class parameter table */ 56 57 typedef struct { /* type definition for generic class-specific parameters */ 58 int pc_clparms[PC_CLINFOSZ]; 59 } pc_clparms_t; 60 61 static int map_gp_to_rtpri(pri_t); 62 63 /* 64 * cache priocntl information on scheduling classes by policy 65 */ 66 int 67 get_info_by_policy(int policy) 68 { 69 char *pccname; 70 struct pcclass *pccp; 71 72 if (policy < 0) { 73 errno = EINVAL; 74 return (-1); 75 } 76 77 switch (policy) { 78 case SCHED_FIFO: 79 case SCHED_RR: 80 pccp = &rt_class; 81 pccname = "RT"; 82 break; 83 case SCHED_OTHER: 84 pccp = &ts_class; 85 pccname = "TS"; 86 break; 87 case SCHED_SYS: 88 pccp = &sys_class; 89 pccname = "sys"; 90 break; 91 case SCHED_IA: 92 pccp = &ia_class; 93 pccname = "IA"; 94 break; 95 default: 96 return (policy); 97 } 98 if (pccp->pcc_state != 0) { 99 if (pccp->pcc_state < 0) 100 errno = ENOSYS; 101 return (pccp->pcc_state); 102 } 103 104 /* get class's info */ 105 (void) strcpy(pccp->pcc_info.pc_clname, pccname); 106 if (policy == SCHED_SYS) 107 pccp->pcc_info.pc_cid = 0; 108 else if (priocntl(P_PID, 0, PC_GETCID, (caddr_t)&(pccp->pcc_info)) < 0) 109 return (-1); 110 111 if (policy == SCHED_FIFO || policy == SCHED_RR) { 112 pcadmin_t pcadmin; 113 rtadmin_t rtadmin; 114 size_t rtdpsize; 115 116 /* get RT class dispatch table in rt_dptbl */ 117 pcadmin.pc_cid = rt_class.pcc_info.pc_cid; 118 pcadmin.pc_cladmin = (caddr_t)&rtadmin; 119 rtadmin.rt_cmd = RT_GETDPSIZE; 120 if (priocntl(P_PID, 0, PC_ADMIN, (caddr_t)&pcadmin) < 0) 121 return (-1); 122 rtdpsize = (size_t)(rtadmin.rt_ndpents * sizeof (rtdpent_t)); 123 if (rt_dptbl == NULL && 124 (rt_dptbl = lmalloc(rtdpsize)) == NULL) { 125 errno = EAGAIN; 126 return (-1); 127 } 128 rtadmin.rt_dpents = rt_dptbl; 129 rtadmin.rt_cmd = RT_GETDPTBL; 130 if (priocntl(P_PID, 0, PC_ADMIN, (caddr_t)&pcadmin) < 0) 131 return (-1); 132 pccp->pcc_primin = 0; 133 pccp->pcc_primax = ((rtinfo_t *)rt_class.pcc_info.pc_clinfo)-> 134 rt_maxpri; 135 } else if (policy == SCHED_OTHER) { 136 pri_t prio; 137 138 prio = ((tsinfo_t *)ts_class.pcc_info.pc_clinfo)->ts_maxupri/3; 139 pccp->pcc_primin = -prio; 140 pccp->pcc_primax = prio; 141 } else { 142 /* non-RT scheduling class */ 143 pcpri_t pcpri; 144 145 /* need RT class info before we can translate priorities */ 146 if (get_info_by_policy(SCHED_FIFO) < 0) 147 return (-1); 148 /* 149 * get class's global priority's min, max, and 150 * translate them into RT priority level (index) via rt_dptbl. 151 */ 152 pcpri.pc_cid = pccp->pcc_info.pc_cid; 153 if (priocntl(0, 0, PC_GETPRIRANGE, (caddr_t)&pcpri) < 0) 154 return (-1); 155 pccp->pcc_primax = map_gp_to_rtpri(pcpri.pc_clpmax); 156 pccp->pcc_primin = map_gp_to_rtpri(pcpri.pc_clpmin); 157 } 158 159 pccp->pcc_state = 1; 160 return (1); 161 } 162 163 /* 164 * Translate global scheduling priority to RT class's user priority. 165 * Use the gp values in the rt_dptbl to do a reverse mapping 166 * of a given gpri value relative to the index range of rt_dptbl. 167 */ 168 static int 169 map_gp_to_rtpri(pri_t gpri) 170 { 171 rtdpent_t *rtdp; 172 pri_t pri; 173 174 if (gpri <= rt_dptbl[rt_class.pcc_primin].rt_globpri) { 175 pri = gpri - rt_dptbl[rt_class.pcc_primin].rt_globpri + \ 176 rt_class.pcc_primin; 177 } else if (gpri >= rt_dptbl[rt_class.pcc_primax].rt_globpri) { 178 pri = gpri - rt_dptbl[rt_class.pcc_primax].rt_globpri + \ 179 rt_class.pcc_primax; 180 } else { 181 pri = rt_class.pcc_primin + 1; 182 for (rtdp = rt_dptbl+1; rtdp->rt_globpri < gpri; ++rtdp, ++pri) 183 ; 184 if (rtdp->rt_globpri > gpri) 185 --pri; 186 } 187 188 return (pri); 189 } 190 191 /* 192 * Translate RT class's user priority to global scheduling priority. 193 */ 194 pri_t 195 map_rtpri_to_gp(pri_t pri) 196 { 197 rtdpent_t *rtdp; 198 pri_t gpri; 199 200 if (rt_class.pcc_state == 0) 201 (void) get_info_by_policy(SCHED_FIFO); 202 203 /* First case is the default case, other two are seldomly taken */ 204 if (pri <= rt_dptbl[rt_class.pcc_primin].rt_globpri) { 205 gpri = pri + rt_dptbl[rt_class.pcc_primin].rt_globpri - 206 rt_class.pcc_primin; 207 } else if (pri >= rt_dptbl[rt_class.pcc_primax].rt_globpri) { 208 gpri = pri + rt_dptbl[rt_class.pcc_primax].rt_globpri - 209 rt_class.pcc_primax; 210 } else { 211 gpri = rt_dptbl[rt_class.pcc_primin].rt_globpri + 1; 212 for (rtdp = rt_dptbl+1; rtdp->rt_globpri < pri; ++rtdp, ++gpri) 213 ; 214 if (rtdp->rt_globpri > pri) 215 --gpri; 216 } 217 return (gpri); 218 } 219 220 static int 221 get_info_by_class(id_t classid) 222 { 223 pcinfo_t pcinfo; 224 225 /* determine if we already know this classid */ 226 if (rt_class.pcc_state > 0 && rt_class.pcc_info.pc_cid == classid) 227 return (1); 228 if (ts_class.pcc_state > 0 && ts_class.pcc_info.pc_cid == classid) 229 return (1); 230 if (sys_class.pcc_state > 0 && sys_class.pcc_info.pc_cid == classid) 231 return (1); 232 if (ia_class.pcc_state > 0 && ia_class.pcc_info.pc_cid == classid) 233 return (1); 234 235 pcinfo.pc_cid = classid; 236 if (priocntl(0, 0, PC_GETCLINFO, (caddr_t)&pcinfo) < 0) { 237 if (classid == 0) /* no kernel info for sys class */ 238 return (get_info_by_policy(SCHED_SYS)); 239 return (-1); 240 } 241 242 if (rt_class.pcc_state == 0 && strcmp(pcinfo.pc_clname, "RT") == 0) 243 return (get_info_by_policy(SCHED_FIFO)); 244 if (ts_class.pcc_state == 0 && strcmp(pcinfo.pc_clname, "TS") == 0) 245 return (get_info_by_policy(SCHED_OTHER)); 246 if (ia_class.pcc_state == 0 && strcmp(pcinfo.pc_clname, "IA") == 0) 247 return (get_info_by_policy(SCHED_IA)); 248 249 return (1); 250 } 251 252 int 253 sched_setparam(pid_t pid, const struct sched_param *param) 254 { 255 pri_t prio = param->sched_priority; 256 pcparms_t pcparm; 257 tsparms_t *tsp; 258 tsinfo_t *tsi; 259 int scale; 260 261 if (pid < 0) { 262 errno = ESRCH; 263 return (-1); 264 } 265 if (pid == 0) 266 pid = P_MYID; 267 268 /* get process's current scheduling policy */ 269 pcparm.pc_cid = PC_CLNULL; 270 if (priocntl(P_PID, pid, PC_GETPARMS, (caddr_t)&pcparm) == -1) 271 return (-1); 272 if (get_info_by_class(pcparm.pc_cid) < 0) 273 return (-1); 274 275 if (pcparm.pc_cid == rt_class.pcc_info.pc_cid) { 276 /* SCHED_FIFO or SCHED_RR policy */ 277 if (prio < rt_class.pcc_primin || prio > rt_class.pcc_primax) { 278 errno = EINVAL; 279 return (-1); 280 } 281 ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs = RT_NOCHANGE; 282 ((rtparms_t *)pcparm.pc_clparms)->rt_pri = prio; 283 } else if (pcparm.pc_cid == ts_class.pcc_info.pc_cid) { 284 /* SCHED_OTHER policy */ 285 tsi = (tsinfo_t *)ts_class.pcc_info.pc_clinfo; 286 scale = tsi->ts_maxupri; 287 tsp = (tsparms_t *)pcparm.pc_clparms; 288 tsp->ts_uprilim = tsp->ts_upri = -(scale * prio) / 20; 289 } else { 290 /* 291 * policy is not defined by POSIX.4. 292 * just pass parameter data through to priocntl. 293 * param should contain an image of class-specific parameters 294 * (after the sched_priority member). 295 */ 296 *((pc_clparms_t *)pcparm.pc_clparms) = 297 *((pc_clparms_t *)(&(param->sched_priority)+1)); 298 } 299 300 return ((int)priocntl(P_PID, pid, PC_SETPARMS, (caddr_t)&pcparm)); 301 } 302 303 int 304 sched_getparam(pid_t pid, struct sched_param *param) 305 { 306 pcparms_t pcparm; 307 pri_t prio; 308 int scale; 309 tsinfo_t *tsi; 310 311 if (pid < 0) { 312 errno = ESRCH; 313 return (-1); 314 } 315 if (pid == 0) 316 pid = P_MYID; 317 318 pcparm.pc_cid = PC_CLNULL; 319 if (priocntl(P_PID, pid, PC_GETPARMS, (caddr_t)&pcparm) == -1) 320 return (-1); 321 if (get_info_by_class(pcparm.pc_cid) < 0) 322 return (-1); 323 324 if (pcparm.pc_cid == rt_class.pcc_info.pc_cid) { 325 param->sched_priority = 326 ((rtparms_t *)pcparm.pc_clparms)->rt_pri; 327 } else if (pcparm.pc_cid == ts_class.pcc_info.pc_cid) { 328 param->sched_nicelim = 329 ((tsparms_t *)pcparm.pc_clparms)->ts_uprilim; 330 prio = param->sched_nice = 331 ((tsparms_t *)pcparm.pc_clparms)->ts_upri; 332 tsi = (tsinfo_t *)ts_class.pcc_info.pc_clinfo; 333 scale = tsi->ts_maxupri; 334 if (scale == 0) 335 param->sched_priority = 0; 336 else 337 param->sched_priority = -(prio * 20) / scale; 338 } else { 339 /* 340 * policy is not defined by POSIX.4 341 * just return a copy of pcparams_t image in param. 342 */ 343 *((pc_clparms_t *)(&(param->sched_priority)+1)) = 344 *((pc_clparms_t *)pcparm.pc_clparms); 345 param->sched_priority = 346 sched_get_priority_min((int)(pcparm.pc_cid + _SCHED_NEXT)); 347 } 348 349 return (0); 350 } 351 352 int 353 sched_setscheduler(pid_t pid, int policy, const struct sched_param *param) 354 { 355 pri_t prio = param->sched_priority; 356 pcparms_t pcparm; 357 int oldpolicy; 358 tsinfo_t *tsi; 359 tsparms_t *tsp; 360 int scale; 361 362 if ((oldpolicy = sched_getscheduler(pid)) < 0) 363 return (-1); 364 365 if (pid == 0) 366 pid = P_MYID; 367 368 if (get_info_by_policy(policy) < 0) { 369 errno = EINVAL; 370 return (-1); 371 } 372 373 switch (policy) { 374 case SCHED_FIFO: 375 case SCHED_RR: 376 if (prio < rt_class.pcc_primin || prio > rt_class.pcc_primax) { 377 errno = EINVAL; 378 return (-1); 379 } 380 pcparm.pc_cid = rt_class.pcc_info.pc_cid; 381 ((rtparms_t *)pcparm.pc_clparms)->rt_pri = prio; 382 ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs = 383 (policy == SCHED_RR ? RT_TQDEF : RT_TQINF); 384 break; 385 386 case SCHED_OTHER: 387 pcparm.pc_cid = ts_class.pcc_info.pc_cid; 388 tsi = (tsinfo_t *)ts_class.pcc_info.pc_clinfo; 389 scale = tsi->ts_maxupri; 390 tsp = (tsparms_t *)pcparm.pc_clparms; 391 tsp->ts_uprilim = tsp->ts_upri = -(scale * prio) / 20; 392 break; 393 394 default: 395 switch (policy) { 396 case SCHED_SYS: 397 pcparm.pc_cid = sys_class.pcc_info.pc_cid; 398 break; 399 case SCHED_IA: 400 pcparm.pc_cid = ia_class.pcc_info.pc_cid; 401 break; 402 default: 403 pcparm.pc_cid = policy - _SCHED_NEXT; 404 break; 405 } 406 /* 407 * policy is not defined by POSIX.4. 408 * just pass parameter data through to priocntl. 409 * param should contain an image of class-specific parameters 410 * (after the sched_priority member). 411 */ 412 *((pc_clparms_t *)pcparm.pc_clparms) = 413 *((pc_clparms_t *)&(param->sched_priority)+1); 414 } 415 416 /* setting scheduling policy & parameters for the process */ 417 if (priocntl(P_PID, pid, PC_SETPARMS, (caddr_t)&pcparm) == -1) 418 return (-1); 419 420 return (oldpolicy); 421 } 422 423 int 424 sched_getscheduler(pid_t pid) 425 { 426 pcparms_t pcparm; 427 int policy; 428 429 if (pid < 0) { 430 errno = ESRCH; 431 return (-1); 432 } 433 if (pid == 0) 434 pid = P_MYID; 435 436 /* get scheduling policy & parameters for the process */ 437 pcparm.pc_cid = PC_CLNULL; 438 if (priocntl(P_PID, pid, PC_GETPARMS, (caddr_t)&pcparm) == -1) 439 return (-1); 440 if (get_info_by_class(pcparm.pc_cid) < 0) 441 return (-1); 442 443 if (pcparm.pc_cid == rt_class.pcc_info.pc_cid) 444 policy = ((((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs == 445 RT_TQINF ? SCHED_FIFO : SCHED_RR)); 446 else if (pcparm.pc_cid == ts_class.pcc_info.pc_cid) 447 policy = SCHED_OTHER; 448 else if (pcparm.pc_cid == sys_class.pcc_info.pc_cid) 449 policy = SCHED_SYS; 450 else if (pcparm.pc_cid == ia_class.pcc_info.pc_cid) 451 policy = SCHED_IA; 452 else { 453 /* 454 * policy is not defined by POSIX.4 455 * return a unique dot4 policy id. 456 */ 457 policy = (int)(_SCHED_NEXT + pcparm.pc_cid); 458 } 459 460 return (policy); 461 } 462 463 int 464 sched_yield(void) 465 { 466 thr_yield(); 467 return (0); 468 } 469 470 int 471 sched_get_priority_max(int policy) 472 { 473 pcpri_t pcpri; 474 475 if (get_info_by_policy(policy) < 0) 476 return (-1); 477 478 if (policy == SCHED_FIFO || policy == SCHED_RR) 479 return (rt_class.pcc_primax); 480 else if (policy == SCHED_OTHER) 481 return (ts_class.pcc_primax); 482 else if (policy == SCHED_SYS) 483 return (sys_class.pcc_primax); 484 else if (policy == SCHED_IA) 485 return (ia_class.pcc_primax); 486 else { /* policy not in POSIX.4 */ 487 pcpri.pc_cid = policy - _SCHED_NEXT; 488 if (priocntl(0, 0, PC_GETPRIRANGE, (caddr_t)&pcpri) == 0) 489 return (map_gp_to_rtpri(pcpri.pc_clpmax)); 490 } 491 492 errno = EINVAL; 493 return (-1); 494 } 495 496 int 497 sched_get_priority_min(int policy) 498 { 499 pcpri_t pcpri; 500 501 if (get_info_by_policy(policy) < 0) 502 return (-1); 503 504 if (policy == SCHED_FIFO || policy == SCHED_RR) 505 return (rt_class.pcc_primin); 506 else if (policy == SCHED_OTHER) 507 return (ts_class.pcc_primin); 508 else if (policy == SCHED_SYS) 509 return (sys_class.pcc_primin); 510 else if (policy == SCHED_IA) 511 return (ia_class.pcc_primin); 512 else { /* policy not in POSIX.4 */ 513 pcpri.pc_cid = policy - _SCHED_NEXT; 514 if (priocntl(0, 0, PC_GETPRIRANGE, (caddr_t)&pcpri) == 0) 515 return (map_gp_to_rtpri(pcpri.pc_clpmin)); 516 } 517 518 errno = EINVAL; 519 return (-1); 520 } 521 522 int 523 sched_rr_get_interval(pid_t pid, timespec_t *interval) 524 { 525 pcparms_t pcparm; 526 527 if (pid < 0) { 528 errno = ESRCH; 529 return (-1); 530 } 531 if (pid == 0) 532 pid = P_MYID; 533 534 if (get_info_by_policy(SCHED_RR) < 0) 535 return (-1); 536 537 pcparm.pc_cid = PC_CLNULL; 538 if (priocntl(P_PID, pid, PC_GETPARMS, (caddr_t)&pcparm) == -1) 539 return (-1); 540 541 if (pcparm.pc_cid == rt_class.pcc_info.pc_cid && 542 (((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs != RT_TQINF)) { 543 /* SCHED_RR */ 544 interval->tv_sec = ((rtparms_t *)pcparm.pc_clparms)->rt_tqsecs; 545 interval->tv_nsec = 546 ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs; 547 return (0); 548 } 549 550 errno = EINVAL; 551 return (-1); 552 } 553