Merge remote-tracking branch 'origin/master'
[unleashed/lotheac.git] / kernel / sched / rt / rt.c
blobe6dc4c2c73197f7e6a8ed51cad5c4e93245bdbaf
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 * Copyright 2013 Joyent, Inc. All rights reserved.
28 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
29 /* All Rights Reserved */
31 #include <sys/types.h>
32 #include <sys/param.h>
33 #include <sys/sysmacros.h>
34 #include <sys/cred.h>
35 #include <sys/proc.h>
36 #include <sys/pcb.h>
37 #include <sys/signal.h>
38 #include <sys/user.h>
39 #include <sys/priocntl.h>
40 #include <sys/class.h>
41 #include <sys/disp.h>
42 #include <sys/procset.h>
43 #include <sys/cmn_err.h>
44 #include <sys/debug.h>
45 #include <sys/rt.h>
46 #include <sys/rtpriocntl.h>
47 #include <sys/kmem.h>
48 #include <sys/systm.h>
49 #include <sys/schedctl.h>
50 #include <sys/errno.h>
51 #include <sys/cpuvar.h>
52 #include <sys/vmsystm.h>
53 #include <sys/time.h>
54 #include <sys/policy.h>
55 #include <sys/sdt.h>
56 #include <sys/cpupart.h>
57 #include <sys/modctl.h>
59 static pri_t rt_init(id_t, int, classfuncs_t **);
61 static struct sclass csw = {
62 "RT",
63 rt_init,
67 static struct modlsched modlsched = {
68 &mod_schedops, "realtime scheduling class", &csw
71 static struct modlinkage modlinkage = {
72 MODREV_1, (void *)&modlsched, NULL
75 int
76 _init()
78 return (mod_install(&modlinkage));
81 int
82 _fini()
84 return (EBUSY); /* don't remove RT for now */
87 int
88 _info(struct modinfo *modinfop)
90 return (mod_info(&modlinkage, modinfop));
95 * Class specific code for the real-time class
99 * Extern declarations for variables defined in the rt master file
101 #define RTMAXPRI 59
103 pri_t rt_maxpri = RTMAXPRI; /* maximum real-time priority */
104 rtdpent_t *rt_dptbl; /* real-time dispatcher parameter table */
107 * control flags (kparms->rt_cflags).
109 #define RT_DOPRI 0x01 /* change priority */
110 #define RT_DOTQ 0x02 /* change RT time quantum */
111 #define RT_DOSIG 0x04 /* change RT time quantum signal */
113 static int rt_admin(caddr_t, cred_t *);
114 static int rt_enterclass(kthread_t *, id_t, void *, cred_t *, void *);
115 static int rt_fork(kthread_t *, kthread_t *, void *);
116 static int rt_getclinfo(void *);
117 static int rt_getclpri(pcpri_t *);
118 static int rt_parmsin(void *);
119 static int rt_parmsout(void *, pc_vaparms_t *);
120 static int rt_vaparmsin(void *, pc_vaparms_t *);
121 static int rt_vaparmsout(void *, pc_vaparms_t *);
122 static int rt_parmsset(kthread_t *, void *, id_t, cred_t *);
123 static int rt_donice(kthread_t *, cred_t *, int, int *);
124 static int rt_doprio(kthread_t *, cred_t *, int, int *);
125 static void rt_exitclass(void *);
126 static int rt_canexit(kthread_t *, cred_t *);
127 static void rt_forkret(kthread_t *, kthread_t *);
128 static void rt_nullsys();
129 static void rt_parmsget(kthread_t *, void *);
130 static void rt_preempt(kthread_t *);
131 static void rt_setrun(kthread_t *);
132 static void rt_tick(kthread_t *);
133 static void rt_wakeup(kthread_t *);
134 static pri_t rt_globpri(kthread_t *);
135 static void rt_yield(kthread_t *);
136 static int rt_alloc(void **, int);
137 static void rt_free(void *);
139 static void rt_change_priority(kthread_t *, rtproc_t *);
141 static id_t rt_cid; /* real-time class ID */
142 static rtproc_t rt_plisthead; /* dummy rtproc at head of rtproc list */
143 static kmutex_t rt_dptblock; /* protects realtime dispatch table */
144 static kmutex_t rt_list_lock; /* protects RT thread list */
146 extern rtdpent_t *rt_getdptbl(void);
148 static struct classfuncs rt_classfuncs = {
149 /* class ops */
150 rt_admin,
151 rt_getclinfo,
152 rt_parmsin,
153 rt_parmsout,
154 rt_vaparmsin,
155 rt_vaparmsout,
156 rt_getclpri,
157 rt_alloc,
158 rt_free,
159 /* thread ops */
160 rt_enterclass,
161 rt_exitclass,
162 rt_canexit,
163 rt_fork,
164 rt_forkret,
165 rt_parmsget,
166 rt_parmsset,
167 rt_nullsys, /* stop */
168 rt_nullsys, /* exit */
169 rt_nullsys, /* active */
170 rt_nullsys, /* inactive */
171 rt_nullsys, /* trapret */
172 rt_preempt,
173 rt_setrun,
174 rt_nullsys, /* sleep */
175 rt_tick,
176 rt_wakeup,
177 rt_donice,
178 rt_globpri,
179 rt_nullsys, /* set_process_group */
180 rt_yield,
181 rt_doprio,
185 * Real-time class initialization. Called by dispinit() at boot time.
186 * We can ignore the clparmsz argument since we know that the smallest
187 * possible parameter buffer is big enough for us.
189 /* ARGSUSED */
190 pri_t
191 rt_init(id_t cid, int clparmsz, classfuncs_t **clfuncspp)
193 rt_dptbl = rt_getdptbl();
194 rt_cid = cid; /* Record our class ID */
197 * Initialize the rtproc list.
199 rt_plisthead.rt_next = rt_plisthead.rt_prev = &rt_plisthead;
202 * We're required to return a pointer to our classfuncs
203 * structure and the highest global priority value we use.
205 *clfuncspp = &rt_classfuncs;
206 mutex_init(&rt_dptblock, NULL, MUTEX_DEFAULT, NULL);
207 mutex_init(&rt_list_lock, NULL, MUTEX_DEFAULT, NULL);
208 return (rt_dptbl[rt_maxpri].rt_globpri);
212 * Get or reset the rt_dptbl values per the user's request.
214 /* ARGSUSED */
215 static int
216 rt_admin(caddr_t uaddr, cred_t *reqpcredp)
218 rtadmin_t rtadmin;
219 rtdpent_t *tmpdpp;
220 size_t userdpsz;
221 size_t rtdpsz;
222 int i;
224 if (get_udatamodel() == DATAMODEL_NATIVE) {
225 if (copyin(uaddr, &rtadmin, sizeof (rtadmin_t)))
226 return (EFAULT);
228 #ifdef _SYSCALL32_IMPL
229 else {
230 /* rtadmin struct from ILP32 callers */
231 rtadmin32_t rtadmin32;
232 if (copyin(uaddr, &rtadmin32, sizeof (rtadmin32_t)))
233 return (EFAULT);
234 rtadmin.rt_dpents =
235 (struct rtdpent *)(uintptr_t)rtadmin32.rt_dpents;
236 rtadmin.rt_ndpents = rtadmin32.rt_ndpents;
237 rtadmin.rt_cmd = rtadmin32.rt_cmd;
239 #endif /* _SYSCALL32_IMPL */
241 rtdpsz = (rt_maxpri + 1) * sizeof (rtdpent_t);
243 switch (rtadmin.rt_cmd) {
245 case RT_GETDPSIZE:
246 rtadmin.rt_ndpents = rt_maxpri + 1;
248 if (get_udatamodel() == DATAMODEL_NATIVE) {
249 if (copyout(&rtadmin, uaddr, sizeof (rtadmin_t)))
250 return (EFAULT);
252 #ifdef _SYSCALL32_IMPL
253 else {
254 /* return rtadmin struct to ILP32 callers */
255 rtadmin32_t rtadmin32;
256 rtadmin32.rt_dpents =
257 (caddr32_t)(uintptr_t)rtadmin.rt_dpents;
258 rtadmin32.rt_ndpents = rtadmin.rt_ndpents;
259 rtadmin32.rt_cmd = rtadmin.rt_cmd;
260 if (copyout(&rtadmin32, uaddr, sizeof (rtadmin32_t)))
261 return (EFAULT);
263 #endif /* _SYSCALL32_IMPL */
265 break;
267 case RT_GETDPTBL:
268 userdpsz = MIN(rtadmin.rt_ndpents * sizeof (rtdpent_t),
269 rtdpsz);
270 if (copyout(rt_dptbl, rtadmin.rt_dpents, userdpsz))
271 return (EFAULT);
272 rtadmin.rt_ndpents = userdpsz / sizeof (rtdpent_t);
274 if (get_udatamodel() == DATAMODEL_NATIVE) {
275 if (copyout(&rtadmin, uaddr, sizeof (rtadmin_t)))
276 return (EFAULT);
278 #ifdef _SYSCALL32_IMPL
279 else {
280 /* return rtadmin struct to ILP32 callers */
281 rtadmin32_t rtadmin32;
282 rtadmin32.rt_dpents =
283 (caddr32_t)(uintptr_t)rtadmin.rt_dpents;
284 rtadmin32.rt_ndpents = rtadmin.rt_ndpents;
285 rtadmin32.rt_cmd = rtadmin.rt_cmd;
286 if (copyout(&rtadmin32, uaddr, sizeof (rtadmin32_t)))
287 return (EFAULT);
289 #endif /* _SYSCALL32_IMPL */
290 break;
292 case RT_SETDPTBL:
294 * We require that the requesting process has sufficient
295 * priveleges. We also require that the table supplied by
296 * the user exactly match the current rt_dptbl in size.
298 if (secpolicy_dispadm(reqpcredp) != 0)
299 return (EPERM);
300 if (rtadmin.rt_ndpents * sizeof (rtdpent_t) != rtdpsz)
301 return (EINVAL);
304 * We read the user supplied table into a temporary buffer
305 * where the time quantum values are validated before
306 * being copied to the rt_dptbl.
308 tmpdpp = kmem_alloc(rtdpsz, KM_SLEEP);
309 if (copyin(rtadmin.rt_dpents, tmpdpp, rtdpsz)) {
310 kmem_free(tmpdpp, rtdpsz);
311 return (EFAULT);
313 for (i = 0; i < rtadmin.rt_ndpents; i++) {
316 * Validate the user supplied time quantum values.
318 if (tmpdpp[i].rt_quantum <= 0 &&
319 tmpdpp[i].rt_quantum != RT_TQINF) {
320 kmem_free(tmpdpp, rtdpsz);
321 return (EINVAL);
326 * Copy the user supplied values over the current rt_dptbl
327 * values. The rt_globpri member is read-only so we don't
328 * overwrite it.
330 mutex_enter(&rt_dptblock);
331 for (i = 0; i < rtadmin.rt_ndpents; i++)
332 rt_dptbl[i].rt_quantum = tmpdpp[i].rt_quantum;
333 mutex_exit(&rt_dptblock);
334 kmem_free(tmpdpp, rtdpsz);
335 break;
337 default:
338 return (EINVAL);
340 return (0);
345 * Allocate a real-time class specific proc structure and
346 * initialize it with the parameters supplied. Also move thread
347 * to specified real-time priority.
349 /* ARGSUSED */
350 static int
351 rt_enterclass(kthread_t *t, id_t cid, void *parmsp, cred_t *reqpcredp,
352 void *bufp)
354 rtkparms_t *rtkparmsp = (rtkparms_t *)parmsp;
355 rtproc_t *rtpp;
358 * For a thread to enter the real-time class the thread
359 * which initiates the request must be privileged.
360 * This may have been checked previously but if our
361 * caller passed us a credential structure we assume it
362 * hasn't and we check it here.
364 if (reqpcredp != NULL && secpolicy_setpriority(reqpcredp) != 0)
365 return (EPERM);
367 rtpp = (rtproc_t *)bufp;
368 ASSERT(rtpp != NULL);
371 * If this thread's lwp is swapped out, it will be brought in
372 * when it is put onto the runqueue.
374 * Now, Initialize the rtproc structure.
376 if (rtkparmsp == NULL) {
378 * Use default values
380 rtpp->rt_pri = 0;
381 rtpp->rt_pquantum = rt_dptbl[0].rt_quantum;
382 rtpp->rt_tqsignal = 0;
383 } else {
385 * Use supplied values
387 if ((rtkparmsp->rt_cflags & RT_DOPRI) == 0)
388 rtpp->rt_pri = 0;
389 else
390 rtpp->rt_pri = rtkparmsp->rt_pri;
392 if (rtkparmsp->rt_tqntm == RT_TQINF)
393 rtpp->rt_pquantum = RT_TQINF;
394 else if (rtkparmsp->rt_tqntm == RT_TQDEF ||
395 (rtkparmsp->rt_cflags & RT_DOTQ) == 0)
396 rtpp->rt_pquantum = rt_dptbl[rtpp->rt_pri].rt_quantum;
397 else
398 rtpp->rt_pquantum = rtkparmsp->rt_tqntm;
400 if ((rtkparmsp->rt_cflags & RT_DOSIG) == 0)
401 rtpp->rt_tqsignal = 0;
402 else
403 rtpp->rt_tqsignal = rtkparmsp->rt_tqsig;
405 rtpp->rt_flags = 0;
406 rtpp->rt_tp = t;
408 * Reset thread priority
410 thread_lock(t);
411 t->t_clfuncs = &(sclass[cid].cl_funcs->thread);
412 t->t_cid = cid;
413 t->t_cldata = (void *)rtpp;
414 t->t_schedflag &= ~TS_RUNQMATCH;
415 rt_change_priority(t, rtpp);
416 thread_unlock(t);
418 * Link new structure into rtproc list
420 mutex_enter(&rt_list_lock);
421 rtpp->rt_next = rt_plisthead.rt_next;
422 rtpp->rt_prev = &rt_plisthead;
423 rt_plisthead.rt_next->rt_prev = rtpp;
424 rt_plisthead.rt_next = rtpp;
425 mutex_exit(&rt_list_lock);
426 return (0);
431 * Free rtproc structure of thread.
433 static void
434 rt_exitclass(void *procp)
436 rtproc_t *rtprocp = (rtproc_t *)procp;
438 mutex_enter(&rt_list_lock);
439 rtprocp->rt_prev->rt_next = rtprocp->rt_next;
440 rtprocp->rt_next->rt_prev = rtprocp->rt_prev;
441 mutex_exit(&rt_list_lock);
442 kmem_free(rtprocp, sizeof (rtproc_t));
447 * Allocate and initialize real-time class specific
448 * proc structure for child.
450 /* ARGSUSED */
451 static int
452 rt_fork(kthread_t *t, kthread_t *ct, void *bufp)
454 rtproc_t *prtpp;
455 rtproc_t *crtpp;
457 ASSERT(MUTEX_HELD(&ttoproc(t)->p_lock));
460 * Initialize child's rtproc structure
462 crtpp = (rtproc_t *)bufp;
463 ASSERT(crtpp != NULL);
464 prtpp = (rtproc_t *)t->t_cldata;
465 thread_lock(t);
466 crtpp->rt_timeleft = crtpp->rt_pquantum = prtpp->rt_pquantum;
467 crtpp->rt_pri = prtpp->rt_pri;
468 crtpp->rt_flags = prtpp->rt_flags & ~RTBACKQ;
469 crtpp->rt_tqsignal = prtpp->rt_tqsignal;
471 crtpp->rt_tp = ct;
472 thread_unlock(t);
475 * Link new structure into rtproc list
477 ct->t_cldata = (void *)crtpp;
478 mutex_enter(&rt_list_lock);
479 crtpp->rt_next = rt_plisthead.rt_next;
480 crtpp->rt_prev = &rt_plisthead;
481 rt_plisthead.rt_next->rt_prev = crtpp;
482 rt_plisthead.rt_next = crtpp;
483 mutex_exit(&rt_list_lock);
484 return (0);
489 * The child goes to the back of its dispatcher queue while the
490 * parent continues to run after a real time thread forks.
492 /* ARGSUSED */
493 static void
494 rt_forkret(kthread_t *t, kthread_t *ct)
496 proc_t *pp = ttoproc(t);
497 proc_t *cp = ttoproc(ct);
499 ASSERT(t == curthread);
500 ASSERT(MUTEX_HELD(&pidlock));
503 * Grab the child's p_lock before dropping pidlock to ensure
504 * the process does not disappear before we set it running.
506 mutex_enter(&cp->p_lock);
507 mutex_exit(&pidlock);
508 continuelwps(cp);
509 mutex_exit(&cp->p_lock);
511 mutex_enter(&pp->p_lock);
512 continuelwps(pp);
513 mutex_exit(&pp->p_lock);
518 * Get information about the real-time class into the buffer
519 * pointed to by rtinfop. The maximum configured real-time
520 * priority is the only information we supply. We ignore the
521 * class and credential arguments because anyone can have this
522 * information.
524 /* ARGSUSED */
525 static int
526 rt_getclinfo(void *infop)
528 rtinfo_t *rtinfop = (rtinfo_t *)infop;
529 rtinfop->rt_maxpri = rt_maxpri;
530 return (0);
534 * Return the user mode scheduling priority range.
536 static int
537 rt_getclpri(pcpri_t *pcprip)
539 pcprip->pc_clpmax = rt_maxpri;
540 pcprip->pc_clpmin = 0;
541 return (0);
544 static void
545 rt_nullsys()
549 /* ARGSUSED */
550 static int
551 rt_canexit(kthread_t *t, cred_t *cred)
554 * Thread can always leave RT class
556 return (0);
560 * Get the real-time scheduling parameters of the thread pointed to by
561 * rtprocp into the buffer pointed to by rtkparmsp.
563 static void
564 rt_parmsget(kthread_t *t, void *parmsp)
566 rtproc_t *rtprocp = (rtproc_t *)t->t_cldata;
567 rtkparms_t *rtkparmsp = (rtkparms_t *)parmsp;
569 rtkparmsp->rt_pri = rtprocp->rt_pri;
570 rtkparmsp->rt_tqntm = rtprocp->rt_pquantum;
571 rtkparmsp->rt_tqsig = rtprocp->rt_tqsignal;
577 * Check the validity of the real-time parameters in the buffer
578 * pointed to by rtprmsp.
579 * We convert the rtparms buffer from the user supplied format to
580 * our internal format (i.e. time quantum expressed in ticks).
582 static int
583 rt_parmsin(void *prmsp)
585 rtparms_t *rtprmsp = (rtparms_t *)prmsp;
586 longlong_t ticks;
587 uint_t cflags;
590 * First check the validity of parameters and convert
591 * the buffer to kernel format.
593 if ((rtprmsp->rt_pri < 0 || rtprmsp->rt_pri > rt_maxpri) &&
594 rtprmsp->rt_pri != RT_NOCHANGE)
595 return (EINVAL);
597 cflags = (rtprmsp->rt_pri != RT_NOCHANGE ? RT_DOPRI : 0);
599 if ((rtprmsp->rt_tqsecs == 0 && rtprmsp->rt_tqnsecs == 0) ||
600 rtprmsp->rt_tqnsecs >= NANOSEC)
601 return (EINVAL);
603 if (rtprmsp->rt_tqnsecs != RT_NOCHANGE)
604 cflags |= RT_DOTQ;
606 if (rtprmsp->rt_tqnsecs >= 0) {
607 if ((ticks = SEC_TO_TICK((longlong_t)rtprmsp->rt_tqsecs) +
608 NSEC_TO_TICK_ROUNDUP(rtprmsp->rt_tqnsecs)) > INT_MAX)
609 return (ERANGE);
611 ((rtkparms_t *)rtprmsp)->rt_tqntm = (int)ticks;
612 } else {
613 if (rtprmsp->rt_tqnsecs != RT_NOCHANGE &&
614 rtprmsp->rt_tqnsecs != RT_TQINF &&
615 rtprmsp->rt_tqnsecs != RT_TQDEF)
616 return (EINVAL);
618 ((rtkparms_t *)rtprmsp)->rt_tqntm = rtprmsp->rt_tqnsecs;
620 ((rtkparms_t *)rtprmsp)->rt_cflags = cflags;
622 return (0);
627 * Check the validity of the real-time parameters in the pc_vaparms_t
628 * structure vaparmsp and put them in the buffer pointed to by rtprmsp.
629 * pc_vaparms_t contains (key, value) pairs of parameter.
630 * rt_vaparmsin() is the variable parameter version of rt_parmsin().
632 static int
633 rt_vaparmsin(void *prmsp, pc_vaparms_t *vaparmsp)
635 uint_t secs = 0;
636 uint_t cnt;
637 int nsecs = 0;
638 int priflag, secflag, nsecflag, sigflag;
639 longlong_t ticks;
640 rtkparms_t *rtprmsp = (rtkparms_t *)prmsp;
641 pc_vaparm_t *vpp = &vaparmsp->pc_parms[0];
645 * First check the validity of parameters and convert them
646 * from the user supplied format to the internal format.
648 priflag = secflag = nsecflag = sigflag = 0;
649 rtprmsp->rt_cflags = 0;
651 if (vaparmsp->pc_vaparmscnt > PC_VAPARMCNT)
652 return (EINVAL);
654 for (cnt = 0; cnt < vaparmsp->pc_vaparmscnt; cnt++, vpp++) {
656 switch (vpp->pc_key) {
657 case RT_KY_PRI:
658 if (priflag++)
659 return (EINVAL);
660 rtprmsp->rt_cflags |= RT_DOPRI;
661 rtprmsp->rt_pri = (pri_t)vpp->pc_parm;
662 if (rtprmsp->rt_pri < 0 || rtprmsp->rt_pri > rt_maxpri)
663 return (EINVAL);
664 break;
666 case RT_KY_TQSECS:
667 if (secflag++)
668 return (EINVAL);
669 rtprmsp->rt_cflags |= RT_DOTQ;
670 secs = (uint_t)vpp->pc_parm;
671 break;
673 case RT_KY_TQNSECS:
674 if (nsecflag++)
675 return (EINVAL);
676 rtprmsp->rt_cflags |= RT_DOTQ;
677 nsecs = (int)vpp->pc_parm;
678 break;
680 case RT_KY_TQSIG:
681 if (sigflag++)
682 return (EINVAL);
683 rtprmsp->rt_cflags |= RT_DOSIG;
684 rtprmsp->rt_tqsig = (int)vpp->pc_parm;
685 if (rtprmsp->rt_tqsig < 0 || rtprmsp->rt_tqsig >= NSIG)
686 return (EINVAL);
687 break;
689 default:
690 return (EINVAL);
694 if (vaparmsp->pc_vaparmscnt == 0) {
696 * Use default parameters.
698 rtprmsp->rt_pri = 0;
699 rtprmsp->rt_tqntm = RT_TQDEF;
700 rtprmsp->rt_tqsig = 0;
701 rtprmsp->rt_cflags = RT_DOPRI | RT_DOTQ | RT_DOSIG;
702 } else if ((rtprmsp->rt_cflags & RT_DOTQ) != 0) {
703 if ((secs == 0 && nsecs == 0) || nsecs >= NANOSEC)
704 return (EINVAL);
706 if (nsecs >= 0) {
707 if ((ticks = SEC_TO_TICK((longlong_t)secs) +
708 NSEC_TO_TICK_ROUNDUP(nsecs)) > INT_MAX)
709 return (ERANGE);
711 rtprmsp->rt_tqntm = (int)ticks;
712 } else {
713 if (nsecs != RT_TQINF && nsecs != RT_TQDEF)
714 return (EINVAL);
715 rtprmsp->rt_tqntm = nsecs;
719 return (0);
723 * Do required processing on the real-time parameter buffer
724 * before it is copied out to the user.
725 * All we have to do is convert the buffer from kernel to user format
726 * (i.e. convert time quantum from ticks to seconds-nanoseconds).
728 /* ARGSUSED */
729 static int
730 rt_parmsout(void *prmsp, pc_vaparms_t *vaparmsp)
732 rtkparms_t *rtkprmsp = (rtkparms_t *)prmsp;
734 if (vaparmsp != NULL)
735 return (0);
737 if (rtkprmsp->rt_tqntm < 0) {
739 * Quantum field set to special value (e.g. RT_TQINF)
741 ((rtparms_t *)rtkprmsp)->rt_tqnsecs = rtkprmsp->rt_tqntm;
742 ((rtparms_t *)rtkprmsp)->rt_tqsecs = 0;
743 } else {
744 /* Convert quantum from ticks to seconds-nanoseconds */
746 timestruc_t ts;
747 TICK_TO_TIMESTRUC(rtkprmsp->rt_tqntm, &ts);
748 ((rtparms_t *)rtkprmsp)->rt_tqsecs = ts.tv_sec;
749 ((rtparms_t *)rtkprmsp)->rt_tqnsecs = ts.tv_nsec;
752 return (0);
757 * Copy all selected real-time class parameters to the user.
758 * The parameters are specified by a key.
760 static int
761 rt_vaparmsout(void *prmsp, pc_vaparms_t *vaparmsp)
763 rtkparms_t *rtkprmsp = (rtkparms_t *)prmsp;
764 timestruc_t ts;
765 uint_t cnt;
766 uint_t secs;
767 int nsecs;
768 int priflag, secflag, nsecflag, sigflag;
769 pc_vaparm_t *vpp = &vaparmsp->pc_parms[0];
771 ASSERT(MUTEX_NOT_HELD(&curproc->p_lock));
773 priflag = secflag = nsecflag = sigflag = 0;
775 if (vaparmsp->pc_vaparmscnt > PC_VAPARMCNT)
776 return (EINVAL);
778 if (rtkprmsp->rt_tqntm < 0) {
780 * Quantum field set to special value (e.g. RT_TQINF).
782 secs = 0;
783 nsecs = rtkprmsp->rt_tqntm;
784 } else {
786 * Convert quantum from ticks to seconds-nanoseconds.
788 TICK_TO_TIMESTRUC(rtkprmsp->rt_tqntm, &ts);
789 secs = ts.tv_sec;
790 nsecs = ts.tv_nsec;
794 for (cnt = 0; cnt < vaparmsp->pc_vaparmscnt; cnt++, vpp++) {
796 switch (vpp->pc_key) {
797 case RT_KY_PRI:
798 if (priflag++)
799 return (EINVAL);
800 if (copyout(&rtkprmsp->rt_pri,
801 (caddr_t)(uintptr_t)vpp->pc_parm, sizeof (pri_t)))
802 return (EFAULT);
803 break;
805 case RT_KY_TQSECS:
806 if (secflag++)
807 return (EINVAL);
808 if (copyout(&secs, (caddr_t)(uintptr_t)vpp->pc_parm,
809 sizeof (uint_t)))
810 return (EFAULT);
811 break;
813 case RT_KY_TQNSECS:
814 if (nsecflag++)
815 return (EINVAL);
816 if (copyout(&nsecs, (caddr_t)(uintptr_t)vpp->pc_parm,
817 sizeof (int)))
818 return (EFAULT);
819 break;
821 case RT_KY_TQSIG:
822 if (sigflag++)
823 return (EINVAL);
824 if (copyout(&rtkprmsp->rt_tqsig,
825 (caddr_t)(uintptr_t)vpp->pc_parm, sizeof (int)))
826 return (EFAULT);
827 break;
829 default:
830 return (EINVAL);
834 return (0);
839 * Set the scheduling parameters of the thread pointed to by rtprocp
840 * to those specified in the buffer pointed to by rtkprmsp.
841 * Note that the parameters are expected to be in kernel format
842 * (i.e. time quantm expressed in ticks). Real time parameters copied
843 * in from the user should be processed by rt_parmsin() before they are
844 * passed to this function.
846 static int
847 rt_parmsset(kthread_t *tx, void *prmsp, id_t reqpcid, cred_t *reqpcredp)
849 rtkparms_t *rtkprmsp = (rtkparms_t *)prmsp;
850 rtproc_t *rtpp = (rtproc_t *)tx->t_cldata;
852 ASSERT(MUTEX_HELD(&(ttoproc(tx))->p_lock));
855 * Basic permissions enforced by generic kernel code
856 * for all classes require that a thread attempting
857 * to change the scheduling parameters of a target thread
858 * be privileged or have a real or effective UID
859 * matching that of the target thread. We are not
860 * called unless these basic permission checks have
861 * already passed. The real-time class requires in addition
862 * that the requesting thread be real-time unless it is privileged.
863 * This may also have been checked previously but if our caller
864 * passes us a credential structure we assume it hasn't and
865 * we check it here.
867 if (reqpcredp != NULL && reqpcid != rt_cid &&
868 secpolicy_raisepriority(reqpcredp) != 0)
869 return (EPERM);
871 thread_lock(tx);
872 if ((rtkprmsp->rt_cflags & RT_DOPRI) != 0) {
873 rtpp->rt_pri = rtkprmsp->rt_pri;
874 rt_change_priority(tx, rtpp);
876 if (rtkprmsp->rt_tqntm == RT_TQINF)
877 rtpp->rt_pquantum = RT_TQINF;
878 else if (rtkprmsp->rt_tqntm == RT_TQDEF)
879 rtpp->rt_timeleft = rtpp->rt_pquantum =
880 rt_dptbl[rtpp->rt_pri].rt_quantum;
881 else if ((rtkprmsp->rt_cflags & RT_DOTQ) != 0)
882 rtpp->rt_timeleft = rtpp->rt_pquantum = rtkprmsp->rt_tqntm;
884 if ((rtkprmsp->rt_cflags & RT_DOSIG) != 0)
885 rtpp->rt_tqsignal = rtkprmsp->rt_tqsig;
887 thread_unlock(tx);
888 return (0);
893 * Arrange for thread to be placed in appropriate location
894 * on dispatcher queue. Runs at splhi() since the clock
895 * interrupt can cause RTBACKQ to be set.
897 static void
898 rt_preempt(kthread_t *t)
900 rtproc_t *rtpp = (rtproc_t *)(t->t_cldata);
902 ASSERT(THREAD_LOCK_HELD(t));
904 if ((rtpp->rt_flags & RTBACKQ) != 0) {
905 rtpp->rt_timeleft = rtpp->rt_pquantum;
906 rtpp->rt_flags &= ~RTBACKQ;
907 setbackdq(t);
908 } else
909 setfrontdq(t);
914 * Return the global priority associated with this rt_pri.
916 static pri_t
917 rt_globpri(kthread_t *t)
919 rtproc_t *rtprocp = (rtproc_t *)t->t_cldata;
920 return (rt_dptbl[rtprocp->rt_pri].rt_globpri);
923 static void
924 rt_setrun(kthread_t *t)
926 rtproc_t *rtpp = (rtproc_t *)(t->t_cldata);
928 ASSERT(THREAD_LOCK_HELD(t));
930 rtpp->rt_timeleft = rtpp->rt_pquantum;
931 rtpp->rt_flags &= ~RTBACKQ;
932 setbackdq(t);
936 * Check for time slice expiration (unless thread has infinite time
937 * slice). If time slice has expired arrange for thread to be preempted
938 * and placed on back of queue.
940 static void
941 rt_tick(kthread_t *t)
943 rtproc_t *rtpp = (rtproc_t *)(t->t_cldata);
945 ASSERT(MUTEX_HELD(&(ttoproc(t))->p_lock));
947 thread_lock(t);
948 if ((rtpp->rt_pquantum != RT_TQINF && --rtpp->rt_timeleft == 0) ||
949 (t->t_state == TS_ONPROC && DISP_MUST_SURRENDER(t))) {
950 if (rtpp->rt_timeleft == 0 && rtpp->rt_tqsignal) {
951 thread_unlock(t);
952 sigtoproc(ttoproc(t), t, rtpp->rt_tqsignal);
953 thread_lock(t);
955 rtpp->rt_flags |= RTBACKQ;
956 cpu_surrender(t);
958 thread_unlock(t);
963 * Place the thread waking up on the dispatcher queue.
965 static void
966 rt_wakeup(kthread_t *t)
968 rtproc_t *rtpp = (rtproc_t *)(t->t_cldata);
970 ASSERT(THREAD_LOCK_HELD(t));
972 rtpp->rt_timeleft = rtpp->rt_pquantum;
973 rtpp->rt_flags &= ~RTBACKQ;
974 setbackdq(t);
977 static void
978 rt_yield(kthread_t *t)
980 rtproc_t *rtpp = (rtproc_t *)(t->t_cldata);
982 ASSERT(t == curthread);
983 ASSERT(THREAD_LOCK_HELD(t));
985 rtpp->rt_flags &= ~RTBACKQ;
986 setbackdq(t);
989 /* ARGSUSED */
990 static int
991 rt_donice(kthread_t *t, cred_t *cr, int incr, int *retvalp)
993 return (EINVAL);
997 * Increment the priority of the specified thread by incr and
998 * return the new value in *retvalp.
1000 static int
1001 rt_doprio(kthread_t *t, cred_t *cr, int incr, int *retvalp)
1003 int newpri;
1004 rtproc_t *rtpp = (rtproc_t *)(t->t_cldata);
1005 rtkparms_t rtkparms;
1007 /* If there's no change to the priority, just return current setting */
1008 if (incr == 0) {
1009 *retvalp = rtpp->rt_pri;
1010 return (0);
1013 newpri = rtpp->rt_pri + incr;
1014 if (newpri > rt_maxpri || newpri < 0)
1015 return (EINVAL);
1017 *retvalp = newpri;
1018 rtkparms.rt_pri = newpri;
1019 rtkparms.rt_tqntm = RT_NOCHANGE;
1020 rtkparms.rt_tqsig = 0;
1021 rtkparms.rt_cflags = RT_DOPRI;
1022 return (rt_parmsset(t, &rtkparms, rt_cid, cr));
1025 static int
1026 rt_alloc(void **p, int flag)
1028 void *bufp;
1029 bufp = kmem_alloc(sizeof (rtproc_t), flag);
1030 if (bufp == NULL) {
1031 return (ENOMEM);
1032 } else {
1033 *p = bufp;
1034 return (0);
1038 static void
1039 rt_free(void *bufp)
1041 if (bufp)
1042 kmem_free(bufp, sizeof (rtproc_t));
1045 static void
1046 rt_change_priority(kthread_t *t, rtproc_t *rtpp)
1048 pri_t new_pri;
1050 ASSERT(THREAD_LOCK_HELD(t));
1052 new_pri = rt_dptbl[rtpp->rt_pri].rt_globpri;
1054 t->t_cpri = rtpp->rt_pri;
1055 if (t == curthread || t->t_state == TS_ONPROC) {
1056 cpu_t *cp = t->t_disp_queue->disp_cpu;
1057 THREAD_CHANGE_PRI(t, new_pri);
1058 if (t == cp->cpu_dispthread)
1059 cp->cpu_dispatch_pri = DISP_PRIO(t);
1060 if (DISP_MUST_SURRENDER(t)) {
1061 rtpp->rt_flags |= RTBACKQ;
1062 cpu_surrender(t);
1063 } else {
1064 rtpp->rt_timeleft = rtpp->rt_pquantum;
1066 } else {
1068 * When the priority of a thread is changed,
1069 * it may be necessary to adjust its position
1070 * on a sleep queue or dispatch queue. The
1071 * function thread_change_pri() accomplishes this.
1073 if (thread_change_pri(t, new_pri, 0)) {
1075 * The thread was on a run queue.
1076 * Reset its CPU timeleft.
1078 rtpp->rt_timeleft = rtpp->rt_pquantum;
1079 } else {
1080 rtpp->rt_flags |= RTBACKQ;