| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License (the "License"). |
| * You may not use this file except in compliance with the License. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| */ |
| |
| /* |
| * Copyright 2008 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| * Copyright 2013 Joyent, Inc. All rights reserved. |
| */ |
| |
| /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ |
| /* All Rights Reserved */ |
| |
| #include <sys/types.h> |
| #include <sys/param.h> |
| #include <sys/sysmacros.h> |
| #include <sys/cred.h> |
| #include <sys/proc.h> |
| #include <sys/pcb.h> |
| #include <sys/signal.h> |
| #include <sys/user.h> |
| #include <sys/priocntl.h> |
| #include <sys/class.h> |
| #include <sys/disp.h> |
| #include <sys/procset.h> |
| #include <sys/cmn_err.h> |
| #include <sys/debug.h> |
| #include <sys/rt.h> |
| #include <sys/rtpriocntl.h> |
| #include <sys/kmem.h> |
| #include <sys/systm.h> |
| #include <sys/schedctl.h> |
| #include <sys/errno.h> |
| #include <sys/cpuvar.h> |
| #include <sys/vmsystm.h> |
| #include <sys/time.h> |
| #include <sys/policy.h> |
| #include <sys/sdt.h> |
| #include <sys/cpupart.h> |
| #include <sys/modctl.h> |
| |
| static pri_t rt_init(id_t, int, classfuncs_t **); |
| |
| static struct sclass csw = { |
| "RT", |
| rt_init, |
| 0 |
| }; |
| |
| static struct modlsched modlsched = { |
| &mod_schedops, "realtime scheduling class", &csw |
| }; |
| |
| static struct modlinkage modlinkage = { |
| MODREV_1, (void *)&modlsched, NULL |
| }; |
| |
| int |
| _init() |
| { |
| return (mod_install(&modlinkage)); |
| } |
| |
| int |
| _fini() |
| { |
| return (EBUSY); /* don't remove RT for now */ |
| } |
| |
| int |
| _info(struct modinfo *modinfop) |
| { |
| return (mod_info(&modlinkage, modinfop)); |
| } |
| |
| |
| /* |
| * Class specific code for the real-time class |
| */ |
| |
| /* |
| * Extern declarations for variables defined in the rt master file |
| */ |
| #define RTMAXPRI 59 |
| |
| pri_t rt_maxpri = RTMAXPRI; /* maximum real-time priority */ |
| rtdpent_t *rt_dptbl; /* real-time dispatcher parameter table */ |
| |
| /* |
| * control flags (kparms->rt_cflags). |
| */ |
| #define RT_DOPRI 0x01 /* change priority */ |
| #define RT_DOTQ 0x02 /* change RT time quantum */ |
| #define RT_DOSIG 0x04 /* change RT time quantum signal */ |
| |
| static int rt_admin(caddr_t, cred_t *); |
| static int rt_enterclass(kthread_t *, id_t, void *, cred_t *, void *); |
| static int rt_fork(kthread_t *, kthread_t *, void *); |
| static int rt_getclinfo(void *); |
| static int rt_getclpri(pcpri_t *); |
| static int rt_parmsin(void *); |
| static int rt_parmsout(void *, pc_vaparms_t *); |
| static int rt_vaparmsin(void *, pc_vaparms_t *); |
| static int rt_vaparmsout(void *, pc_vaparms_t *); |
| static int rt_parmsset(kthread_t *, void *, id_t, cred_t *); |
| static int rt_donice(kthread_t *, cred_t *, int, int *); |
| static int rt_doprio(kthread_t *, cred_t *, int, int *); |
| static void rt_exitclass(void *); |
| static int rt_canexit(kthread_t *, cred_t *); |
| static void rt_forkret(kthread_t *, kthread_t *); |
| static void rt_nullsys(); |
| static void rt_parmsget(kthread_t *, void *); |
| static void rt_preempt(kthread_t *); |
| static void rt_setrun(kthread_t *); |
| static void rt_tick(kthread_t *); |
| static void rt_wakeup(kthread_t *); |
| static pri_t rt_swapin(kthread_t *, int); |
| static pri_t rt_swapout(kthread_t *, int); |
| static pri_t rt_globpri(kthread_t *); |
| static void rt_yield(kthread_t *); |
| static int rt_alloc(void **, int); |
| static void rt_free(void *); |
| |
| static void rt_change_priority(kthread_t *, rtproc_t *); |
| |
| static id_t rt_cid; /* real-time class ID */ |
| static rtproc_t rt_plisthead; /* dummy rtproc at head of rtproc list */ |
| static kmutex_t rt_dptblock; /* protects realtime dispatch table */ |
| static kmutex_t rt_list_lock; /* protects RT thread list */ |
| |
| extern rtdpent_t *rt_getdptbl(void); |
| |
| static struct classfuncs rt_classfuncs = { |
| /* class ops */ |
| rt_admin, |
| rt_getclinfo, |
| rt_parmsin, |
| rt_parmsout, |
| rt_vaparmsin, |
| rt_vaparmsout, |
| rt_getclpri, |
| rt_alloc, |
| rt_free, |
| /* thread ops */ |
| rt_enterclass, |
| rt_exitclass, |
| rt_canexit, |
| rt_fork, |
| rt_forkret, |
| rt_parmsget, |
| rt_parmsset, |
| rt_nullsys, /* stop */ |
| rt_nullsys, /* exit */ |
| rt_nullsys, /* active */ |
| rt_nullsys, /* inactive */ |
| rt_swapin, |
| rt_swapout, |
| rt_nullsys, /* trapret */ |
| rt_preempt, |
| rt_setrun, |
| rt_nullsys, /* sleep */ |
| rt_tick, |
| rt_wakeup, |
| rt_donice, |
| rt_globpri, |
| rt_nullsys, /* set_process_group */ |
| rt_yield, |
| rt_doprio, |
| }; |
| |
| /* |
| * Real-time class initialization. Called by dispinit() at boot time. |
| * We can ignore the clparmsz argument since we know that the smallest |
| * possible parameter buffer is big enough for us. |
| */ |
| /* ARGSUSED */ |
| pri_t |
| rt_init(id_t cid, int clparmsz, classfuncs_t **clfuncspp) |
| { |
| rt_dptbl = rt_getdptbl(); |
| rt_cid = cid; /* Record our class ID */ |
| |
| /* |
| * Initialize the rtproc list. |
| */ |
| rt_plisthead.rt_next = rt_plisthead.rt_prev = &rt_plisthead; |
| |
| /* |
| * We're required to return a pointer to our classfuncs |
| * structure and the highest global priority value we use. |
| */ |
| *clfuncspp = &rt_classfuncs; |
| mutex_init(&rt_dptblock, NULL, MUTEX_DEFAULT, NULL); |
| mutex_init(&rt_list_lock, NULL, MUTEX_DEFAULT, NULL); |
| return (rt_dptbl[rt_maxpri].rt_globpri); |
| } |
| |
| /* |
| * Get or reset the rt_dptbl values per the user's request. |
| */ |
| /* ARGSUSED */ |
| static int |
| rt_admin(caddr_t uaddr, cred_t *reqpcredp) |
| { |
| rtadmin_t rtadmin; |
| rtdpent_t *tmpdpp; |
| size_t userdpsz; |
| size_t rtdpsz; |
| int i; |
| |
| if (get_udatamodel() == DATAMODEL_NATIVE) { |
| if (copyin(uaddr, &rtadmin, sizeof (rtadmin_t))) |
| return (EFAULT); |
| } |
| #ifdef _SYSCALL32_IMPL |
| else { |
| /* rtadmin struct from ILP32 callers */ |
| rtadmin32_t rtadmin32; |
| if (copyin(uaddr, &rtadmin32, sizeof (rtadmin32_t))) |
| return (EFAULT); |
| rtadmin.rt_dpents = |
| (struct rtdpent *)(uintptr_t)rtadmin32.rt_dpents; |
| rtadmin.rt_ndpents = rtadmin32.rt_ndpents; |
| rtadmin.rt_cmd = rtadmin32.rt_cmd; |
| } |
| #endif /* _SYSCALL32_IMPL */ |
| |
| rtdpsz = (rt_maxpri + 1) * sizeof (rtdpent_t); |
| |
| switch (rtadmin.rt_cmd) { |
| |
| case RT_GETDPSIZE: |
| rtadmin.rt_ndpents = rt_maxpri + 1; |
| |
| if (get_udatamodel() == DATAMODEL_NATIVE) { |
| if (copyout(&rtadmin, uaddr, sizeof (rtadmin_t))) |
| return (EFAULT); |
| } |
| #ifdef _SYSCALL32_IMPL |
| else { |
| /* return rtadmin struct to ILP32 callers */ |
| rtadmin32_t rtadmin32; |
| rtadmin32.rt_dpents = |
| (caddr32_t)(uintptr_t)rtadmin.rt_dpents; |
| rtadmin32.rt_ndpents = rtadmin.rt_ndpents; |
| rtadmin32.rt_cmd = rtadmin.rt_cmd; |
| if (copyout(&rtadmin32, uaddr, sizeof (rtadmin32_t))) |
| return (EFAULT); |
| } |
| #endif /* _SYSCALL32_IMPL */ |
| |
| break; |
| |
| case RT_GETDPTBL: |
| userdpsz = MIN(rtadmin.rt_ndpents * sizeof (rtdpent_t), |
| rtdpsz); |
| if (copyout(rt_dptbl, rtadmin.rt_dpents, userdpsz)) |
| return (EFAULT); |
| rtadmin.rt_ndpents = userdpsz / sizeof (rtdpent_t); |
| |
| if (get_udatamodel() == DATAMODEL_NATIVE) { |
| if (copyout(&rtadmin, uaddr, sizeof (rtadmin_t))) |
| return (EFAULT); |
| } |
| #ifdef _SYSCALL32_IMPL |
| else { |
| /* return rtadmin struct to ILP32 callers */ |
| rtadmin32_t rtadmin32; |
| rtadmin32.rt_dpents = |
| (caddr32_t)(uintptr_t)rtadmin.rt_dpents; |
| rtadmin32.rt_ndpents = rtadmin.rt_ndpents; |
| rtadmin32.rt_cmd = rtadmin.rt_cmd; |
| if (copyout(&rtadmin32, uaddr, sizeof (rtadmin32_t))) |
| return (EFAULT); |
| } |
| #endif /* _SYSCALL32_IMPL */ |
| break; |
| |
| case RT_SETDPTBL: |
| /* |
| * We require that the requesting process has sufficient |
| * priveleges. We also require that the table supplied by |
| * the user exactly match the current rt_dptbl in size. |
| */ |
| if (secpolicy_dispadm(reqpcredp) != 0) |
| return (EPERM); |
| if (rtadmin.rt_ndpents * sizeof (rtdpent_t) != rtdpsz) |
| return (EINVAL); |
| |
| /* |
| * We read the user supplied table into a temporary buffer |
| * where the time quantum values are validated before |
| * being copied to the rt_dptbl. |
| */ |
| tmpdpp = kmem_alloc(rtdpsz, KM_SLEEP); |
| if (copyin(rtadmin.rt_dpents, tmpdpp, rtdpsz)) { |
| kmem_free(tmpdpp, rtdpsz); |
| return (EFAULT); |
| } |
| for (i = 0; i < rtadmin.rt_ndpents; i++) { |
| |
| /* |
| * Validate the user supplied time quantum values. |
| */ |
| if (tmpdpp[i].rt_quantum <= 0 && |
| tmpdpp[i].rt_quantum != RT_TQINF) { |
| kmem_free(tmpdpp, rtdpsz); |
| return (EINVAL); |
| } |
| } |
| |
| /* |
| * Copy the user supplied values over the current rt_dptbl |
| * values. The rt_globpri member is read-only so we don't |
| * overwrite it. |
| */ |
| mutex_enter(&rt_dptblock); |
| for (i = 0; i < rtadmin.rt_ndpents; i++) |
| rt_dptbl[i].rt_quantum = tmpdpp[i].rt_quantum; |
| mutex_exit(&rt_dptblock); |
| kmem_free(tmpdpp, rtdpsz); |
| break; |
| |
| default: |
| return (EINVAL); |
| } |
| return (0); |
| } |
| |
| |
| /* |
| * Allocate a real-time class specific proc structure and |
| * initialize it with the parameters supplied. Also move thread |
| * to specified real-time priority. |
| */ |
| /* ARGSUSED */ |
| static int |
| rt_enterclass(kthread_t *t, id_t cid, void *parmsp, cred_t *reqpcredp, |
| void *bufp) |
| { |
| rtkparms_t *rtkparmsp = (rtkparms_t *)parmsp; |
| rtproc_t *rtpp; |
| |
| /* |
| * For a thread to enter the real-time class the thread |
| * which initiates the request must be privileged. |
| * This may have been checked previously but if our |
| * caller passed us a credential structure we assume it |
| * hasn't and we check it here. |
| */ |
| if (reqpcredp != NULL && secpolicy_setpriority(reqpcredp) != 0) |
| return (EPERM); |
| |
| rtpp = (rtproc_t *)bufp; |
| ASSERT(rtpp != NULL); |
| |
| /* |
| * If this thread's lwp is swapped out, it will be brought in |
| * when it is put onto the runqueue. |
| * |
| * Now, Initialize the rtproc structure. |
| */ |
| if (rtkparmsp == NULL) { |
| /* |
| * Use default values |
| */ |
| rtpp->rt_pri = 0; |
| rtpp->rt_pquantum = rt_dptbl[0].rt_quantum; |
| rtpp->rt_tqsignal = 0; |
| } else { |
| /* |
| * Use supplied values |
| */ |
| if ((rtkparmsp->rt_cflags & RT_DOPRI) == 0) |
| rtpp->rt_pri = 0; |
| else |
| rtpp->rt_pri = rtkparmsp->rt_pri; |
| |
| if (rtkparmsp->rt_tqntm == RT_TQINF) |
| rtpp->rt_pquantum = RT_TQINF; |
| else if (rtkparmsp->rt_tqntm == RT_TQDEF || |
| (rtkparmsp->rt_cflags & RT_DOTQ) == 0) |
| rtpp->rt_pquantum = rt_dptbl[rtpp->rt_pri].rt_quantum; |
| else |
| rtpp->rt_pquantum = rtkparmsp->rt_tqntm; |
| |
| if ((rtkparmsp->rt_cflags & RT_DOSIG) == 0) |
| rtpp->rt_tqsignal = 0; |
| else |
| rtpp->rt_tqsignal = rtkparmsp->rt_tqsig; |
| } |
| rtpp->rt_flags = 0; |
| rtpp->rt_tp = t; |
| /* |
| * Reset thread priority |
| */ |
| thread_lock(t); |
| t->t_clfuncs = &(sclass[cid].cl_funcs->thread); |
| t->t_cid = cid; |
| t->t_cldata = (void *)rtpp; |
| t->t_schedflag &= ~TS_RUNQMATCH; |
| rt_change_priority(t, rtpp); |
| thread_unlock(t); |
| /* |
| * Link new structure into rtproc list |
| */ |
| mutex_enter(&rt_list_lock); |
| rtpp->rt_next = rt_plisthead.rt_next; |
| rtpp->rt_prev = &rt_plisthead; |
| rt_plisthead.rt_next->rt_prev = rtpp; |
| rt_plisthead.rt_next = rtpp; |
| mutex_exit(&rt_list_lock); |
| return (0); |
| } |
| |
| |
| /* |
| * Free rtproc structure of thread. |
| */ |
| static void |
| rt_exitclass(void *procp) |
| { |
| rtproc_t *rtprocp = (rtproc_t *)procp; |
| |
| mutex_enter(&rt_list_lock); |
| rtprocp->rt_prev->rt_next = rtprocp->rt_next; |
| rtprocp->rt_next->rt_prev = rtprocp->rt_prev; |
| mutex_exit(&rt_list_lock); |
| kmem_free(rtprocp, sizeof (rtproc_t)); |
| } |
| |
| |
| /* |
| * Allocate and initialize real-time class specific |
| * proc structure for child. |
| */ |
| /* ARGSUSED */ |
| static int |
| rt_fork(kthread_t *t, kthread_t *ct, void *bufp) |
| { |
| rtproc_t *prtpp; |
| rtproc_t *crtpp; |
| |
| ASSERT(MUTEX_HELD(&ttoproc(t)->p_lock)); |
| |
| /* |
| * Initialize child's rtproc structure |
| */ |
| crtpp = (rtproc_t *)bufp; |
| ASSERT(crtpp != NULL); |
| prtpp = (rtproc_t *)t->t_cldata; |
| thread_lock(t); |
| crtpp->rt_timeleft = crtpp->rt_pquantum = prtpp->rt_pquantum; |
| crtpp->rt_pri = prtpp->rt_pri; |
| crtpp->rt_flags = prtpp->rt_flags & ~RTBACKQ; |
| crtpp->rt_tqsignal = prtpp->rt_tqsignal; |
| |
| crtpp->rt_tp = ct; |
| thread_unlock(t); |
| |
| /* |
| * Link new structure into rtproc list |
| */ |
| ct->t_cldata = (void *)crtpp; |
| mutex_enter(&rt_list_lock); |
| crtpp->rt_next = rt_plisthead.rt_next; |
| crtpp->rt_prev = &rt_plisthead; |
| rt_plisthead.rt_next->rt_prev = crtpp; |
| rt_plisthead.rt_next = crtpp; |
| mutex_exit(&rt_list_lock); |
| return (0); |
| } |
| |
| |
| /* |
| * The child goes to the back of its dispatcher queue while the |
| * parent continues to run after a real time thread forks. |
| */ |
| /* ARGSUSED */ |
| static void |
| rt_forkret(kthread_t *t, kthread_t *ct) |
| { |
| proc_t *pp = ttoproc(t); |
| proc_t *cp = ttoproc(ct); |
| |
| ASSERT(t == curthread); |
| ASSERT(MUTEX_HELD(&pidlock)); |
| |
| /* |
| * Grab the child's p_lock before dropping pidlock to ensure |
| * the process does not disappear before we set it running. |
| */ |
| mutex_enter(&cp->p_lock); |
| mutex_exit(&pidlock); |
| continuelwps(cp); |
| mutex_exit(&cp->p_lock); |
| |
| mutex_enter(&pp->p_lock); |
| continuelwps(pp); |
| mutex_exit(&pp->p_lock); |
| } |
| |
| |
| /* |
| * Get information about the real-time class into the buffer |
| * pointed to by rtinfop. The maximum configured real-time |
| * priority is the only information we supply. We ignore the |
| * class and credential arguments because anyone can have this |
| * information. |
| */ |
| /* ARGSUSED */ |
| static int |
| rt_getclinfo(void *infop) |
| { |
| rtinfo_t *rtinfop = (rtinfo_t *)infop; |
| rtinfop->rt_maxpri = rt_maxpri; |
| return (0); |
| } |
| |
| /* |
| * Return the user mode scheduling priority range. |
| */ |
| static int |
| rt_getclpri(pcpri_t *pcprip) |
| { |
| pcprip->pc_clpmax = rt_maxpri; |
| pcprip->pc_clpmin = 0; |
| return (0); |
| } |
| |
| static void |
| rt_nullsys() |
| { |
| } |
| |
| /* ARGSUSED */ |
| static int |
| rt_canexit(kthread_t *t, cred_t *cred) |
| { |
| /* |
| * Thread can always leave RT class |
| */ |
| return (0); |
| } |
| |
| /* |
| * Get the real-time scheduling parameters of the thread pointed to by |
| * rtprocp into the buffer pointed to by rtkparmsp. |
| */ |
| static void |
| rt_parmsget(kthread_t *t, void *parmsp) |
| { |
| rtproc_t *rtprocp = (rtproc_t *)t->t_cldata; |
| rtkparms_t *rtkparmsp = (rtkparms_t *)parmsp; |
| |
| rtkparmsp->rt_pri = rtprocp->rt_pri; |
| rtkparmsp->rt_tqntm = rtprocp->rt_pquantum; |
| rtkparmsp->rt_tqsig = rtprocp->rt_tqsignal; |
| } |
| |
| |
| |
| /* |
| * Check the validity of the real-time parameters in the buffer |
| * pointed to by rtprmsp. |
| * We convert the rtparms buffer from the user supplied format to |
| * our internal format (i.e. time quantum expressed in ticks). |
| */ |
| static int |
| rt_parmsin(void *prmsp) |
| { |
| rtparms_t *rtprmsp = (rtparms_t *)prmsp; |
| longlong_t ticks; |
| uint_t cflags; |
| |
| /* |
| * First check the validity of parameters and convert |
| * the buffer to kernel format. |
| */ |
| if ((rtprmsp->rt_pri < 0 || rtprmsp->rt_pri > rt_maxpri) && |
| rtprmsp->rt_pri != RT_NOCHANGE) |
| return (EINVAL); |
| |
| cflags = (rtprmsp->rt_pri != RT_NOCHANGE ? RT_DOPRI : 0); |
| |
| if ((rtprmsp->rt_tqsecs == 0 && rtprmsp->rt_tqnsecs == 0) || |
| rtprmsp->rt_tqnsecs >= NANOSEC) |
| return (EINVAL); |
| |
| if (rtprmsp->rt_tqnsecs != RT_NOCHANGE) |
| cflags |= RT_DOTQ; |
| |
| if (rtprmsp->rt_tqnsecs >= 0) { |
| if ((ticks = SEC_TO_TICK((longlong_t)rtprmsp->rt_tqsecs) + |
| NSEC_TO_TICK_ROUNDUP(rtprmsp->rt_tqnsecs)) > INT_MAX) |
| return (ERANGE); |
| |
| ((rtkparms_t *)rtprmsp)->rt_tqntm = (int)ticks; |
| } else { |
| if (rtprmsp->rt_tqnsecs != RT_NOCHANGE && |
| rtprmsp->rt_tqnsecs != RT_TQINF && |
| rtprmsp->rt_tqnsecs != RT_TQDEF) |
| return (EINVAL); |
| |
| ((rtkparms_t *)rtprmsp)->rt_tqntm = rtprmsp->rt_tqnsecs; |
| } |
| ((rtkparms_t *)rtprmsp)->rt_cflags = cflags; |
| |
| return (0); |
| } |
| |
| |
| /* |
| * Check the validity of the real-time parameters in the pc_vaparms_t |
| * structure vaparmsp and put them in the buffer pointed to by rtprmsp. |
| * pc_vaparms_t contains (key, value) pairs of parameter. |
| * rt_vaparmsin() is the variable parameter version of rt_parmsin(). |
| */ |
| static int |
| rt_vaparmsin(void *prmsp, pc_vaparms_t *vaparmsp) |
| { |
| uint_t secs = 0; |
| uint_t cnt; |
| int nsecs = 0; |
| int priflag, secflag, nsecflag, sigflag; |
| longlong_t ticks; |
| rtkparms_t *rtprmsp = (rtkparms_t *)prmsp; |
| pc_vaparm_t *vpp = &vaparmsp->pc_parms[0]; |
| |
| |
| /* |
| * First check the validity of parameters and convert them |
| * from the user supplied format to the internal format. |
| */ |
| priflag = secflag = nsecflag = sigflag = 0; |
| rtprmsp->rt_cflags = 0; |
| |
| if (vaparmsp->pc_vaparmscnt > PC_VAPARMCNT) |
| return (EINVAL); |
| |
| for (cnt = 0; cnt < vaparmsp->pc_vaparmscnt; cnt++, vpp++) { |
| |
| switch (vpp->pc_key) { |
| case RT_KY_PRI: |
| if (priflag++) |
| return (EINVAL); |
| rtprmsp->rt_cflags |= RT_DOPRI; |
| rtprmsp->rt_pri = (pri_t)vpp->pc_parm; |
| if (rtprmsp->rt_pri < 0 || rtprmsp->rt_pri > rt_maxpri) |
| return (EINVAL); |
| break; |
| |
| case RT_KY_TQSECS: |
| if (secflag++) |
| return (EINVAL); |
| rtprmsp->rt_cflags |= RT_DOTQ; |
| secs = (uint_t)vpp->pc_parm; |
| break; |
| |
| case RT_KY_TQNSECS: |
| if (nsecflag++) |
| return (EINVAL); |
| rtprmsp->rt_cflags |= RT_DOTQ; |
| nsecs = (int)vpp->pc_parm; |
| break; |
| |
| case RT_KY_TQSIG: |
| if (sigflag++) |
| return (EINVAL); |
| rtprmsp->rt_cflags |= RT_DOSIG; |
| rtprmsp->rt_tqsig = (int)vpp->pc_parm; |
| if (rtprmsp->rt_tqsig < 0 || rtprmsp->rt_tqsig >= NSIG) |
| return (EINVAL); |
| break; |
| |
| default: |
| return (EINVAL); |
| } |
| } |
| |
| if (vaparmsp->pc_vaparmscnt == 0) { |
| /* |
| * Use default parameters. |
| */ |
| rtprmsp->rt_pri = 0; |
| rtprmsp->rt_tqntm = RT_TQDEF; |
| rtprmsp->rt_tqsig = 0; |
| rtprmsp->rt_cflags = RT_DOPRI | RT_DOTQ | RT_DOSIG; |
| } else if ((rtprmsp->rt_cflags & RT_DOTQ) != 0) { |
| if ((secs == 0 && nsecs == 0) || nsecs >= NANOSEC) |
| return (EINVAL); |
| |
| if (nsecs >= 0) { |
| if ((ticks = SEC_TO_TICK((longlong_t)secs) + |
| NSEC_TO_TICK_ROUNDUP(nsecs)) > INT_MAX) |
| return (ERANGE); |
| |
| rtprmsp->rt_tqntm = (int)ticks; |
| } else { |
| if (nsecs != RT_TQINF && nsecs != RT_TQDEF) |
| return (EINVAL); |
| rtprmsp->rt_tqntm = nsecs; |
| } |
| } |
| |
| return (0); |
| } |
| |
| /* |
| * Do required processing on the real-time parameter buffer |
| * before it is copied out to the user. |
| * All we have to do is convert the buffer from kernel to user format |
| * (i.e. convert time quantum from ticks to seconds-nanoseconds). |
| */ |
| /* ARGSUSED */ |
| static int |
| rt_parmsout(void *prmsp, pc_vaparms_t *vaparmsp) |
| { |
| rtkparms_t *rtkprmsp = (rtkparms_t *)prmsp; |
| |
| if (vaparmsp != NULL) |
| return (0); |
| |
| if (rtkprmsp->rt_tqntm < 0) { |
| /* |
| * Quantum field set to special value (e.g. RT_TQINF) |
| */ |
| ((rtparms_t *)rtkprmsp)->rt_tqnsecs = rtkprmsp->rt_tqntm; |
| ((rtparms_t *)rtkprmsp)->rt_tqsecs = 0; |
| } else { |
| /* Convert quantum from ticks to seconds-nanoseconds */ |
| |
| timestruc_t ts; |
| TICK_TO_TIMESTRUC(rtkprmsp->rt_tqntm, &ts); |
| ((rtparms_t *)rtkprmsp)->rt_tqsecs = ts.tv_sec; |
| ((rtparms_t *)rtkprmsp)->rt_tqnsecs = ts.tv_nsec; |
| } |
| |
| return (0); |
| } |
| |
| |
| /* |
| * Copy all selected real-time class parameters to the user. |
| * The parameters are specified by a key. |
| */ |
| static int |
| rt_vaparmsout(void *prmsp, pc_vaparms_t *vaparmsp) |
| { |
| rtkparms_t *rtkprmsp = (rtkparms_t *)prmsp; |
| timestruc_t ts; |
| uint_t cnt; |
| uint_t secs; |
| int nsecs; |
| int priflag, secflag, nsecflag, sigflag; |
| pc_vaparm_t *vpp = &vaparmsp->pc_parms[0]; |
| |
| ASSERT(MUTEX_NOT_HELD(&curproc->p_lock)); |
| |
| priflag = secflag = nsecflag = sigflag = 0; |
| |
| if (vaparmsp->pc_vaparmscnt > PC_VAPARMCNT) |
| return (EINVAL); |
| |
| if (rtkprmsp->rt_tqntm < 0) { |
| /* |
| * Quantum field set to special value (e.g. RT_TQINF). |
| */ |
| secs = 0; |
| nsecs = rtkprmsp->rt_tqntm; |
| } else { |
| /* |
| * Convert quantum from ticks to seconds-nanoseconds. |
| */ |
| TICK_TO_TIMESTRUC(rtkprmsp->rt_tqntm, &ts); |
| secs = ts.tv_sec; |
| nsecs = ts.tv_nsec; |
| } |
| |
| |
| for (cnt = 0; cnt < vaparmsp->pc_vaparmscnt; cnt++, vpp++) { |
| |
| switch (vpp->pc_key) { |
| case RT_KY_PRI: |
| if (priflag++) |
| return (EINVAL); |
| if (copyout(&rtkprmsp->rt_pri, |
| (caddr_t)(uintptr_t)vpp->pc_parm, sizeof (pri_t))) |
| return (EFAULT); |
| break; |
| |
| case RT_KY_TQSECS: |
| if (secflag++) |
| return (EINVAL); |
| if (copyout(&secs, (caddr_t)(uintptr_t)vpp->pc_parm, |
| sizeof (uint_t))) |
| return (EFAULT); |
| break; |
| |
| case RT_KY_TQNSECS: |
| if (nsecflag++) |
| return (EINVAL); |
| if (copyout(&nsecs, (caddr_t)(uintptr_t)vpp->pc_parm, |
| sizeof (int))) |
| return (EFAULT); |
| break; |
| |
| case RT_KY_TQSIG: |
| if (sigflag++) |
| return (EINVAL); |
| if (copyout(&rtkprmsp->rt_tqsig, |
| (caddr_t)(uintptr_t)vpp->pc_parm, sizeof (int))) |
| return (EFAULT); |
| break; |
| |
| default: |
| return (EINVAL); |
| } |
| } |
| |
| return (0); |
| } |
| |
| |
| /* |
| * Set the scheduling parameters of the thread pointed to by rtprocp |
| * to those specified in the buffer pointed to by rtkprmsp. |
| * Note that the parameters are expected to be in kernel format |
| * (i.e. time quantm expressed in ticks). Real time parameters copied |
| * in from the user should be processed by rt_parmsin() before they are |
| * passed to this function. |
| */ |
| static int |
| rt_parmsset(kthread_t *tx, void *prmsp, id_t reqpcid, cred_t *reqpcredp) |
| { |
| rtkparms_t *rtkprmsp = (rtkparms_t *)prmsp; |
| rtproc_t *rtpp = (rtproc_t *)tx->t_cldata; |
| |
| ASSERT(MUTEX_HELD(&(ttoproc(tx))->p_lock)); |
| |
| /* |
| * Basic permissions enforced by generic kernel code |
| * for all classes require that a thread attempting |
| * to change the scheduling parameters of a target thread |
| * be privileged or have a real or effective UID |
| * matching that of the target thread. We are not |
| * called unless these basic permission checks have |
| * already passed. The real-time class requires in addition |
| * that the requesting thread be real-time unless it is privileged. |
| * This may also have been checked previously but if our caller |
| * passes us a credential structure we assume it hasn't and |
| * we check it here. |
| */ |
| if (reqpcredp != NULL && reqpcid != rt_cid && |
| secpolicy_raisepriority(reqpcredp) != 0) |
| return (EPERM); |
| |
| thread_lock(tx); |
| if ((rtkprmsp->rt_cflags & RT_DOPRI) != 0) { |
| rtpp->rt_pri = rtkprmsp->rt_pri; |
| rt_change_priority(tx, rtpp); |
| } |
| if (rtkprmsp->rt_tqntm == RT_TQINF) |
| rtpp->rt_pquantum = RT_TQINF; |
| else if (rtkprmsp->rt_tqntm == RT_TQDEF) |
| rtpp->rt_timeleft = rtpp->rt_pquantum = |
| rt_dptbl[rtpp->rt_pri].rt_quantum; |
| else if ((rtkprmsp->rt_cflags & RT_DOTQ) != 0) |
| rtpp->rt_timeleft = rtpp->rt_pquantum = rtkprmsp->rt_tqntm; |
| |
| if ((rtkprmsp->rt_cflags & RT_DOSIG) != 0) |
| rtpp->rt_tqsignal = rtkprmsp->rt_tqsig; |
| |
| thread_unlock(tx); |
| return (0); |
| } |
| |
| |
| /* |
| * Arrange for thread to be placed in appropriate location |
| * on dispatcher queue. Runs at splhi() since the clock |
| * interrupt can cause RTBACKQ to be set. |
| */ |
| static void |
| rt_preempt(kthread_t *t) |
| { |
| rtproc_t *rtpp = (rtproc_t *)(t->t_cldata); |
| klwp_t *lwp; |
| |
| ASSERT(THREAD_LOCK_HELD(t)); |
| |
| /* |
| * If the state is user I allow swapping because I know I won't |
| * be holding any locks. |
| */ |
| if ((lwp = curthread->t_lwp) != NULL && lwp->lwp_state == LWP_USER) |
| t->t_schedflag &= ~TS_DONT_SWAP; |
| if ((rtpp->rt_flags & RTBACKQ) != 0) { |
| rtpp->rt_timeleft = rtpp->rt_pquantum; |
| rtpp->rt_flags &= ~RTBACKQ; |
| setbackdq(t); |
| } else |
| setfrontdq(t); |
| |
| } |
| |
| /* |
| * Return the global priority associated with this rt_pri. |
| */ |
| static pri_t |
| rt_globpri(kthread_t *t) |
| { |
| rtproc_t *rtprocp = (rtproc_t *)t->t_cldata; |
| return (rt_dptbl[rtprocp->rt_pri].rt_globpri); |
| } |
| |
| static void |
| rt_setrun(kthread_t *t) |
| { |
| rtproc_t *rtpp = (rtproc_t *)(t->t_cldata); |
| |
| ASSERT(THREAD_LOCK_HELD(t)); |
| |
| rtpp->rt_timeleft = rtpp->rt_pquantum; |
| rtpp->rt_flags &= ~RTBACKQ; |
| setbackdq(t); |
| } |
| |
| /* |
| * Returns the priority of the thread, -1 if the thread is loaded or ineligible |
| * for swapin. |
| * |
| * FX and RT threads are designed so that they don't swapout; however, it |
| * is possible that while the thread is swapped out and in another class, it |
| * can be changed to FX or RT. Since these threads should be swapped in as |
| * soon as they're runnable, rt_swapin returns SHRT_MAX, and fx_swapin |
| * returns SHRT_MAX - 1, so that it gives deference to any swapped out RT |
| * threads. |
| */ |
| /* ARGSUSED */ |
| static pri_t |
| rt_swapin(kthread_t *t, int flags) |
| { |
| pri_t tpri = -1; |
| |
| ASSERT(THREAD_LOCK_HELD(t)); |
| |
| if (t->t_state == TS_RUN && (t->t_schedflag & TS_LOAD) == 0) { |
| tpri = (pri_t)SHRT_MAX; |
| } |
| |
| return (tpri); |
| } |
| |
| /* |
| * Return an effective priority for swapout. |
| */ |
| /* ARGSUSED */ |
| static pri_t |
| rt_swapout(kthread_t *t, int flags) |
| { |
| ASSERT(THREAD_LOCK_HELD(t)); |
| |
| return (-1); |
| } |
| |
| /* |
| * Check for time slice expiration (unless thread has infinite time |
| * slice). If time slice has expired arrange for thread to be preempted |
| * and placed on back of queue. |
| */ |
| static void |
| rt_tick(kthread_t *t) |
| { |
| rtproc_t *rtpp = (rtproc_t *)(t->t_cldata); |
| |
| ASSERT(MUTEX_HELD(&(ttoproc(t))->p_lock)); |
| |
| thread_lock(t); |
| if ((rtpp->rt_pquantum != RT_TQINF && --rtpp->rt_timeleft == 0) || |
| (t->t_state == TS_ONPROC && DISP_MUST_SURRENDER(t))) { |
| if (rtpp->rt_timeleft == 0 && rtpp->rt_tqsignal) { |
| thread_unlock(t); |
| sigtoproc(ttoproc(t), t, rtpp->rt_tqsignal); |
| thread_lock(t); |
| } |
| rtpp->rt_flags |= RTBACKQ; |
| cpu_surrender(t); |
| } |
| thread_unlock(t); |
| } |
| |
| |
| /* |
| * Place the thread waking up on the dispatcher queue. |
| */ |
| static void |
| rt_wakeup(kthread_t *t) |
| { |
| rtproc_t *rtpp = (rtproc_t *)(t->t_cldata); |
| |
| ASSERT(THREAD_LOCK_HELD(t)); |
| |
| rtpp->rt_timeleft = rtpp->rt_pquantum; |
| rtpp->rt_flags &= ~RTBACKQ; |
| setbackdq(t); |
| } |
| |
| static void |
| rt_yield(kthread_t *t) |
| { |
| rtproc_t *rtpp = (rtproc_t *)(t->t_cldata); |
| |
| ASSERT(t == curthread); |
| ASSERT(THREAD_LOCK_HELD(t)); |
| |
| rtpp->rt_flags &= ~RTBACKQ; |
| setbackdq(t); |
| } |
| |
| /* ARGSUSED */ |
| static int |
| rt_donice(kthread_t *t, cred_t *cr, int incr, int *retvalp) |
| { |
| return (EINVAL); |
| } |
| |
| /* |
| * Increment the priority of the specified thread by incr and |
| * return the new value in *retvalp. |
| */ |
| static int |
| rt_doprio(kthread_t *t, cred_t *cr, int incr, int *retvalp) |
| { |
| int newpri; |
| rtproc_t *rtpp = (rtproc_t *)(t->t_cldata); |
| rtkparms_t rtkparms; |
| |
| /* If there's no change to the priority, just return current setting */ |
| if (incr == 0) { |
| *retvalp = rtpp->rt_pri; |
| return (0); |
| } |
| |
| newpri = rtpp->rt_pri + incr; |
| if (newpri > rt_maxpri || newpri < 0) |
| return (EINVAL); |
| |
| *retvalp = newpri; |
| rtkparms.rt_pri = newpri; |
| rtkparms.rt_tqntm = RT_NOCHANGE; |
| rtkparms.rt_tqsig = 0; |
| rtkparms.rt_cflags = RT_DOPRI; |
| return (rt_parmsset(t, &rtkparms, rt_cid, cr)); |
| } |
| |
| static int |
| rt_alloc(void **p, int flag) |
| { |
| void *bufp; |
| bufp = kmem_alloc(sizeof (rtproc_t), flag); |
| if (bufp == NULL) { |
| return (ENOMEM); |
| } else { |
| *p = bufp; |
| return (0); |
| } |
| } |
| |
| static void |
| rt_free(void *bufp) |
| { |
| if (bufp) |
| kmem_free(bufp, sizeof (rtproc_t)); |
| } |
| |
| static void |
| rt_change_priority(kthread_t *t, rtproc_t *rtpp) |
| { |
| pri_t new_pri; |
| |
| ASSERT(THREAD_LOCK_HELD(t)); |
| |
| new_pri = rt_dptbl[rtpp->rt_pri].rt_globpri; |
| |
| t->t_cpri = rtpp->rt_pri; |
| if (t == curthread || t->t_state == TS_ONPROC) { |
| cpu_t *cp = t->t_disp_queue->disp_cpu; |
| THREAD_CHANGE_PRI(t, new_pri); |
| if (t == cp->cpu_dispthread) |
| cp->cpu_dispatch_pri = DISP_PRIO(t); |
| if (DISP_MUST_SURRENDER(t)) { |
| rtpp->rt_flags |= RTBACKQ; |
| cpu_surrender(t); |
| } else { |
| rtpp->rt_timeleft = rtpp->rt_pquantum; |
| } |
| } else { |
| /* |
| * When the priority of a thread is changed, |
| * it may be necessary to adjust its position |
| * on a sleep queue or dispatch queue. The |
| * function thread_change_pri() accomplishes this. |
| */ |
| if (thread_change_pri(t, new_pri, 0)) { |
| /* |
| * The thread was on a run queue. |
| * Reset its CPU timeleft. |
| */ |
| rtpp->rt_timeleft = rtpp->rt_pquantum; |
| } else { |
| rtpp->rt_flags |= RTBACKQ; |
| } |
| } |
| } |