rt.c revision b33833433bc450712bdb03145caaaa0cfd358f92
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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
* 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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/sysmacros.h>
#include <sys/priocntl.h>
#include <sys/rtpriocntl.h>
"RT",
0
};
};
static struct modlinkage modlinkage = {
};
int
_init()
{
return (mod_install(&modlinkage));
}
int
_fini()
{
return (EBUSY); /* don't remove RT for now */
}
int
{
}
/*
* Class specific code for the real-time class
*/
/*
* Extern declarations for variables defined in the rt master file
*/
#define RTMAXPRI 59
/*
* control flags (kparms->rt_cflags).
*/
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 void rt_exitclass(void *);
static void rt_nullsys();
static void rt_parmsget(kthread_t *, void *);
static void rt_preempt(kthread_t *);
static int rt_alloc(void **, int);
static void rt_free(void *);
extern rtdpent_t *rt_getdptbl(void);
static struct classfuncs rt_classfuncs = {
/* class ops */
/* thread ops */
rt_nullsys, /* stop */
rt_nullsys, /* exit */
rt_nullsys, /* active */
rt_nullsys, /* inactive */
rt_nullsys, /* trapret */
rt_nullsys, /* sleep */
rt_nullsys, /* set_process_group */
};
/*
* 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 */
{
rt_dptbl = rt_getdptbl();
/*
* Initialize the rtproc list.
*/
/*
* We're required to return a pointer to our classfuncs
* structure and the highest global priority value we use.
*/
*clfuncspp = &rt_classfuncs;
}
/*
* Get or reset the rt_dptbl values per the user's request.
*/
/* ARGSUSED */
static int
{
int i;
if (get_udatamodel() == DATAMODEL_NATIVE) {
return (EFAULT);
}
#ifdef _SYSCALL32_IMPL
else {
/* rtadmin struct from ILP32 callers */
return (EFAULT);
}
#endif /* _SYSCALL32_IMPL */
case RT_GETDPSIZE:
if (get_udatamodel() == DATAMODEL_NATIVE) {
return (EFAULT);
}
#ifdef _SYSCALL32_IMPL
else {
/* return rtadmin struct to ILP32 callers */
return (EFAULT);
}
#endif /* _SYSCALL32_IMPL */
break;
case RT_GETDPTBL:
rtdpsz);
return (EFAULT);
if (get_udatamodel() == DATAMODEL_NATIVE) {
return (EFAULT);
}
#ifdef _SYSCALL32_IMPL
else {
/* return rtadmin struct to ILP32 callers */
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);
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.
*/
return (EFAULT);
}
for (i = 0; i < rtadmin.rt_ndpents; i++) {
/*
* Validate the user supplied time quantum values.
*/
if (tmpdpp[i].rt_quantum <= 0 &&
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.
*/
for (i = 0; i < rtadmin.rt_ndpents; i++)
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
void *bufp)
{
/*
* 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.
*/
return (EPERM);
/*
* 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.
*/
/*
* Use default values
*/
rtpp->rt_tqsignal = 0;
} else {
/*
* Use supplied values
*/
else
else
rtpp->rt_tqsignal = 0;
else
}
/*
* Reset thread priority
*/
thread_lock(t);
t->t_schedflag &= ~TS_RUNQMATCH;
rt_change_priority(t, rtpp);
thread_unlock(t);
/*
* Link new structure into rtproc list
*/
return (0);
}
/*
* Free rtproc structure of thread.
*/
static void
rt_exitclass(void *procp)
{
}
/*
* Allocate and initialize real-time class specific
* proc structure for child.
*/
/* ARGSUSED */
static int
{
/*
* Initialize child's rtproc structure
*/
thread_lock(t);
thread_unlock(t);
/*
* Link new structure into rtproc list
*/
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
{
/*
* Grab the child's p_lock before dropping pidlock to ensure
* the process does not disappear before we set it running.
*/
}
/*
* 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)
{
return (0);
}
/*
* Return the global scheduling priority ranges of the realtime
* class in pcpri_t structure.
*/
static int
{
return (0);
}
static void
{
}
/* ARGSUSED */
static int
{
/*
* 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
{
}
/*
* 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)
{
/*
* First check the validity of parameters and convert
* the buffer to kernel format.
*/
return (EINVAL);
return (EINVAL);
if (rtprmsp->rt_tqnsecs >= 0) {
return (ERANGE);
} else {
return (EINVAL);
}
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
{
int nsecs = 0;
/*
* First check the validity of parameters and convert them
* from the user supplied format to the internal format.
*/
return (EINVAL);
case RT_KY_PRI:
if (priflag++)
return (EINVAL);
return (EINVAL);
break;
case RT_KY_TQSECS:
if (secflag++)
return (EINVAL);
break;
case RT_KY_TQNSECS:
if (nsecflag++)
return (EINVAL);
break;
case RT_KY_TQSIG:
if (sigflag++)
return (EINVAL);
return (EINVAL);
break;
default:
return (EINVAL);
}
}
if (vaparmsp->pc_vaparmscnt == 0) {
/*
* Use default parameters.
*/
return (EINVAL);
if (nsecs >= 0) {
return (ERANGE);
} else {
return (EINVAL);
}
}
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
{
return (0);
/*
* Quantum field set to special value (e.g. RT_TQINF)
*/
} else {
/* Convert quantum from ticks to seconds-nanoseconds */
}
return (0);
}
/*
* Copy all selected real-time class parameters to the user.
* The parameters are specified by a key.
*/
static int
{
int nsecs;
return (EINVAL);
/*
* Quantum field set to special value (e.g. RT_TQINF).
*/
secs = 0;
} else {
/*
* Convert quantum from ticks to seconds-nanoseconds.
*/
}
case RT_KY_PRI:
if (priflag++)
return (EINVAL);
return (EFAULT);
break;
case RT_KY_TQSECS:
if (secflag++)
return (EINVAL);
sizeof (uint_t)))
return (EFAULT);
break;
case RT_KY_TQNSECS:
if (nsecflag++)
return (EINVAL);
sizeof (int)))
return (EFAULT);
break;
case RT_KY_TQSIG:
if (sigflag++)
return (EINVAL);
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
{
/*
* 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.
*/
secpolicy_setpriority(reqpcredp) != 0)
return (EPERM);
}
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)
{
ASSERT(THREAD_LOCK_HELD(t));
/*
* If the state is user I allow swapping because I know I won't
* be holding any locks.
*/
t->t_schedflag &= ~TS_DONT_SWAP;
setbackdq(t);
} else
setfrontdq(t);
}
/*
* Return the global priority associated with this rt_pri.
*/
static pri_t
rt_globpri(kthread_t *t)
{
}
static void
{
ASSERT(THREAD_LOCK_HELD(t));
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
{
ASSERT(THREAD_LOCK_HELD(t));
}
return (tpri);
}
/*
* Return an effective priority for swapout.
*/
/* ARGSUSED */
static pri_t
{
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
{
thread_lock(t);
thread_unlock(t);
thread_lock(t);
}
cpu_surrender(t);
}
thread_unlock(t);
}
/*
* Place the thread waking up on the dispatcher queue.
*/
static void
{
ASSERT(THREAD_LOCK_HELD(t));
setbackdq(t);
}
static void
{
ASSERT(THREAD_LOCK_HELD(t));
setbackdq(t);
}
/* ARGSUSED */
static int
{
return (EINVAL);
}
static int
{
void *bufp;
return (ENOMEM);
} else {
*p = bufp;
return (0);
}
}
static void
{
if (bufp)
}
static void
{
ASSERT(THREAD_LOCK_HELD(t));
THREAD_CHANGE_PRI(t, new_pri);
if (t == cp->cpu_dispthread)
if (DISP_MUST_SURRENDER(t)) {
cpu_surrender(t);
} else {
}
} 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.
*/
} else {
}
}
}