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