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 /* 146 * get class's global priority's min, max, and 147 * translate them into RT priority level (index) via rt_dptbl. 148 */ 149 pcpri.pc_cid = pccp->pcc_info.pc_cid; 150 if (priocntl(0, 0, PC_GETPRIRANGE, (caddr_t)&pcpri) < 0) 151 return (-1); 152 pccp->pcc_primax = map_gp_to_rtpri(pcpri.pc_clpmax); 153 pccp->pcc_primin = map_gp_to_rtpri(pcpri.pc_clpmin); 154 } 155 156 pccp->pcc_state = 1; 157 return (1); 158 } 159 160 /* 161 * Translate global scheduling priority to RT class's user priority. 162 * Use the gp values in the rt_dptbl to do a reverse mapping 163 * of a given gpri value relative to the index range of rt_dptbl. 164 */ 165 static int 166 map_gp_to_rtpri(pri_t gpri) 167 { 168 rtdpent_t *rtdp; 169 pri_t pri; 170 171 /* need RT class info before we can translate priorities */ 172 if (rt_dptbl == NULL && get_info_by_policy(SCHED_FIFO) < 0) 173 return (-1); 174 175 if (gpri <= rt_dptbl[rt_class.pcc_primin].rt_globpri) { 176 pri = gpri - rt_dptbl[rt_class.pcc_primin].rt_globpri + \ 177 rt_class.pcc_primin; 178 } else if (gpri >= rt_dptbl[rt_class.pcc_primax].rt_globpri) { 179 pri = gpri - rt_dptbl[rt_class.pcc_primax].rt_globpri + \ 180 rt_class.pcc_primax; 181 } else { 182 pri = rt_class.pcc_primin + 1; 183 for (rtdp = rt_dptbl+1; rtdp->rt_globpri < gpri; ++rtdp, ++pri) 184 ; 185 if (rtdp->rt_globpri > gpri) 186 --pri; 187 } 188 189 return (pri); 190 } 191 192 /* 193 * Translate RT class's user priority to global scheduling priority. 194 */ 195 pri_t 196 map_rtpri_to_gp(pri_t pri) 197 { 198 rtdpent_t *rtdp; 199 pri_t gpri; 200 201 if (rt_class.pcc_state == 0) 202 (void) get_info_by_policy(SCHED_FIFO); 203 204 /* First case is the default case, other two are seldomly taken */ 205 if (pri <= rt_dptbl[rt_class.pcc_primin].rt_globpri) { 206 gpri = pri + rt_dptbl[rt_class.pcc_primin].rt_globpri - 207 rt_class.pcc_primin; 208 } else if (pri >= rt_dptbl[rt_class.pcc_primax].rt_globpri) { 209 gpri = pri + rt_dptbl[rt_class.pcc_primax].rt_globpri - 210 rt_class.pcc_primax; 211 } else { 212 gpri = rt_dptbl[rt_class.pcc_primin].rt_globpri + 1; 213 for (rtdp = rt_dptbl+1; rtdp->rt_globpri < pri; ++rtdp, ++gpri) 214 ; 215 if (rtdp->rt_globpri > pri) 216 --gpri; 217 } 218 return (gpri); 219 } 220 221 static int 222 get_info_by_class(id_t classid) 223 { 224 pcinfo_t pcinfo; 225 226 /* determine if we already know this classid */ 227 if (rt_class.pcc_state > 0 && rt_class.pcc_info.pc_cid == classid) 228 return (1); 229 if (ts_class.pcc_state > 0 && ts_class.pcc_info.pc_cid == classid) 230 return (1); 231 if (sys_class.pcc_state > 0 && sys_class.pcc_info.pc_cid == classid) 232 return (1); 233 if (ia_class.pcc_state > 0 && ia_class.pcc_info.pc_cid == classid) 234 return (1); 235 236 pcinfo.pc_cid = classid; 237 if (priocntl(0, 0, PC_GETCLINFO, (caddr_t)&pcinfo) < 0) { 238 if (classid == 0) /* no kernel info for sys class */ 239 return (get_info_by_policy(SCHED_SYS)); 240 return (-1); 241 } 242 243 if (rt_class.pcc_state == 0 && strcmp(pcinfo.pc_clname, "RT") == 0) 244 return (get_info_by_policy(SCHED_FIFO)); 245 if (ts_class.pcc_state == 0 && strcmp(pcinfo.pc_clname, "TS") == 0) 246 return (get_info_by_policy(SCHED_OTHER)); 247 if (ia_class.pcc_state == 0 && strcmp(pcinfo.pc_clname, "IA") == 0) 248 return (get_info_by_policy(SCHED_IA)); 249 250 return (1); 251 } 252 253 int 254 sched_setparam(pid_t pid, const struct sched_param *param) 255 { 256 pri_t prio = param->sched_priority; 257 pcparms_t pcparm; 258 tsparms_t *tsp; 259 tsinfo_t *tsi; 260 int scale; 261 262 if (pid < 0) { 263 errno = ESRCH; 264 return (-1); 265 } 266 if (pid == 0) 267 pid = P_MYID; 268 269 /* get process's current scheduling policy */ 270 pcparm.pc_cid = PC_CLNULL; 271 if (priocntl(P_PID, pid, PC_GETPARMS, (caddr_t)&pcparm) == -1) 272 return (-1); 273 if (get_info_by_class(pcparm.pc_cid) < 0) 274 return (-1); 275 276 if (pcparm.pc_cid == rt_class.pcc_info.pc_cid) { 277 /* SCHED_FIFO or SCHED_RR policy */ 278 if (prio < rt_class.pcc_primin || prio > rt_class.pcc_primax) { 279 errno = EINVAL; 280 return (-1); 281 } 282 ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs = RT_NOCHANGE; 283 ((rtparms_t *)pcparm.pc_clparms)->rt_pri = prio; 284 } else if (pcparm.pc_cid == ts_class.pcc_info.pc_cid) { 285 /* SCHED_OTHER policy */ 286 tsi = (tsinfo_t *)ts_class.pcc_info.pc_clinfo; 287 scale = tsi->ts_maxupri; 288 tsp = (tsparms_t *)pcparm.pc_clparms; 289 tsp->ts_uprilim = tsp->ts_upri = -(scale * prio) / 20; 290 } else { 291 /* 292 * policy is not defined by POSIX.4. 293 * just pass parameter data through to priocntl. 294 * param should contain an image of class-specific parameters 295 * (after the sched_priority member). 296 */ 297 *((pc_clparms_t *)pcparm.pc_clparms) = 298 *((pc_clparms_t *)(&(param->sched_priority)+1)); 299 } 300 301 return ((int)priocntl(P_PID, pid, PC_SETPARMS, (caddr_t)&pcparm)); 302 } 303 304 int 305 sched_getparam(pid_t pid, struct sched_param *param) 306 { 307 pcparms_t pcparm; 308 pri_t prio; 309 int scale; 310 tsinfo_t *tsi; 311 312 if (pid < 0) { 313 errno = ESRCH; 314 return (-1); 315 } 316 if (pid == 0) 317 pid = P_MYID; 318 319 pcparm.pc_cid = PC_CLNULL; 320 if (priocntl(P_PID, pid, PC_GETPARMS, (caddr_t)&pcparm) == -1) 321 return (-1); 322 if (get_info_by_class(pcparm.pc_cid) < 0) 323 return (-1); 324 325 if (pcparm.pc_cid == rt_class.pcc_info.pc_cid) { 326 param->sched_priority = 327 ((rtparms_t *)pcparm.pc_clparms)->rt_pri; 328 } else if (pcparm.pc_cid == ts_class.pcc_info.pc_cid) { 329 param->sched_nicelim = 330 ((tsparms_t *)pcparm.pc_clparms)->ts_uprilim; 331 prio = param->sched_nice = 332 ((tsparms_t *)pcparm.pc_clparms)->ts_upri; 333 tsi = (tsinfo_t *)ts_class.pcc_info.pc_clinfo; 334 scale = tsi->ts_maxupri; 335 if (scale == 0) 336 param->sched_priority = 0; 337 else 338 param->sched_priority = -(prio * 20) / scale; 339 } else { 340 /* 341 * policy is not defined by POSIX.4 342 * just return a copy of pcparams_t image in param. 343 */ 344 *((pc_clparms_t *)(&(param->sched_priority)+1)) = 345 *((pc_clparms_t *)pcparm.pc_clparms); 346 param->sched_priority = 347 sched_get_priority_min((int)(pcparm.pc_cid + _SCHED_NEXT)); 348 } 349 350 return (0); 351 } 352 353 int 354 sched_setscheduler(pid_t pid, int policy, const struct sched_param *param) 355 { 356 pri_t prio = param->sched_priority; 357 pcparms_t pcparm; 358 int oldpolicy; 359 tsinfo_t *tsi; 360 tsparms_t *tsp; 361 int scale; 362 363 if ((oldpolicy = sched_getscheduler(pid)) < 0) 364 return (-1); 365 366 if (pid == 0) 367 pid = P_MYID; 368 369 if (get_info_by_policy(policy) < 0) { 370 errno = EINVAL; 371 return (-1); 372 } 373 374 switch (policy) { 375 case SCHED_FIFO: 376 case SCHED_RR: 377 if (prio < rt_class.pcc_primin || prio > rt_class.pcc_primax) { 378 errno = EINVAL; 379 return (-1); 380 } 381 pcparm.pc_cid = rt_class.pcc_info.pc_cid; 382 ((rtparms_t *)pcparm.pc_clparms)->rt_pri = prio; 383 ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs = 384 (policy == SCHED_RR ? RT_TQDEF : RT_TQINF); 385 break; 386 387 case SCHED_OTHER: 388 pcparm.pc_cid = ts_class.pcc_info.pc_cid; 389 tsi = (tsinfo_t *)ts_class.pcc_info.pc_clinfo; 390 scale = tsi->ts_maxupri; 391 tsp = (tsparms_t *)pcparm.pc_clparms; 392 tsp->ts_uprilim = tsp->ts_upri = -(scale * prio) / 20; 393 break; 394 395 default: 396 switch (policy) { 397 case SCHED_SYS: 398 pcparm.pc_cid = sys_class.pcc_info.pc_cid; 399 break; 400 case SCHED_IA: 401 pcparm.pc_cid = ia_class.pcc_info.pc_cid; 402 break; 403 default: 404 pcparm.pc_cid = policy - _SCHED_NEXT; 405 break; 406 } 407 /* 408 * policy is not defined by POSIX.4. 409 * just pass parameter data through to priocntl. 410 * param should contain an image of class-specific parameters 411 * (after the sched_priority member). 412 */ 413 *((pc_clparms_t *)pcparm.pc_clparms) = 414 *((pc_clparms_t *)&(param->sched_priority)+1); 415 } 416 417 /* setting scheduling policy & parameters for the process */ 418 if (priocntl(P_PID, pid, PC_SETPARMS, (caddr_t)&pcparm) == -1) 419 return (-1); 420 421 return (oldpolicy); 422 } 423 424 int 425 sched_getscheduler(pid_t pid) 426 { 427 pcparms_t pcparm; 428 int policy; 429 430 if (pid < 0) { 431 errno = ESRCH; 432 return (-1); 433 } 434 if (pid == 0) 435 pid = P_MYID; 436 437 /* get scheduling policy & parameters for the process */ 438 pcparm.pc_cid = PC_CLNULL; 439 if (priocntl(P_PID, pid, PC_GETPARMS, (caddr_t)&pcparm) == -1) 440 return (-1); 441 if (get_info_by_class(pcparm.pc_cid) < 0) 442 return (-1); 443 444 if (pcparm.pc_cid == rt_class.pcc_info.pc_cid) 445 policy = ((((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs == 446 RT_TQINF ? SCHED_FIFO : SCHED_RR)); 447 else if (pcparm.pc_cid == ts_class.pcc_info.pc_cid) 448 policy = SCHED_OTHER; 449 else if (pcparm.pc_cid == sys_class.pcc_info.pc_cid) 450 policy = SCHED_SYS; 451 else if (pcparm.pc_cid == ia_class.pcc_info.pc_cid) 452 policy = SCHED_IA; 453 else { 454 /* 455 * policy is not defined by POSIX.4 456 * return a unique dot4 policy id. 457 */ 458 policy = (int)(_SCHED_NEXT + pcparm.pc_cid); 459 } 460 461 return (policy); 462 } 463 464 int 465 sched_yield(void) 466 { 467 thr_yield(); 468 return (0); 469 } 470 471 int 472 sched_get_priority_max(int policy) 473 { 474 pcpri_t pcpri; 475 476 if (get_info_by_policy(policy) < 0) 477 return (-1); 478 479 if (policy == SCHED_FIFO || policy == SCHED_RR) 480 return (rt_class.pcc_primax); 481 else if (policy == SCHED_OTHER) 482 return (ts_class.pcc_primax); 483 else if (policy == SCHED_SYS) 484 return (sys_class.pcc_primax); 485 else if (policy == SCHED_IA) 486 return (ia_class.pcc_primax); 487 else { /* policy not in POSIX.4 */ 488 pcpri.pc_cid = policy - _SCHED_NEXT; 489 if (priocntl(0, 0, PC_GETPRIRANGE, (caddr_t)&pcpri) == 0) 490 return (map_gp_to_rtpri(pcpri.pc_clpmax)); 491 } 492 493 errno = EINVAL; 494 return (-1); 495 } 496 497 int 498 sched_get_priority_min(int policy) 499 { 500 pcpri_t pcpri; 501 502 if (get_info_by_policy(policy) < 0) 503 return (-1); 504 505 if (policy == SCHED_FIFO || policy == SCHED_RR) 506 return (rt_class.pcc_primin); 507 else if (policy == SCHED_OTHER) 508 return (ts_class.pcc_primin); 509 else if (policy == SCHED_SYS) 510 return (sys_class.pcc_primin); 511 else if (policy == SCHED_IA) 512 return (ia_class.pcc_primin); 513 else { /* policy not in POSIX.4 */ 514 pcpri.pc_cid = policy - _SCHED_NEXT; 515 if (priocntl(0, 0, PC_GETPRIRANGE, (caddr_t)&pcpri) == 0) 516 return (map_gp_to_rtpri(pcpri.pc_clpmin)); 517 } 518 519 errno = EINVAL; 520 return (-1); 521 } 522 523 int 524 sched_rr_get_interval(pid_t pid, timespec_t *interval) 525 { 526 pcparms_t pcparm; 527 528 if (pid < 0) { 529 errno = ESRCH; 530 return (-1); 531 } 532 if (pid == 0) 533 pid = P_MYID; 534 535 if (get_info_by_policy(SCHED_RR) < 0) 536 return (-1); 537 538 pcparm.pc_cid = PC_CLNULL; 539 if (priocntl(P_PID, pid, PC_GETPARMS, (caddr_t)&pcparm) == -1) 540 return (-1); 541 542 if (pcparm.pc_cid == rt_class.pcc_info.pc_cid && 543 (((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs != RT_TQINF)) { 544 /* SCHED_RR */ 545 interval->tv_sec = ((rtparms_t *)pcparm.pc_clparms)->rt_tqsecs; 546 interval->tv_nsec = 547 ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs; 548 return (0); 549 } 550 551 errno = EINVAL; 552 return (-1); 553 } 554