xref: /illumos-gate/usr/src/lib/libc/port/rt/sched.c (revision 79033acb)
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