priocntl.c revision d4204c85a44d2589b9afff2c81db7044e97f2d1d
/*
* 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
* 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 (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/schedctl.h>
/*
* Structure used to pass arguments to the proccmp() function.
* The arguments must be passed in a structure because proccmp()
* is called indirectly through the dotoprocs() function which
* will only pass through a single one word argument.
*/
struct pcmpargs {
int *pcmp_cntp;
};
/*
* Structure used to pass arguments to the setparms() function
* which is called indirectly through dotoprocs().
*/
struct stprmargs {
int stp_error; /* some errors returned here */
};
/*
* A vaparm_t is an int followed by a long long -- this packs differently
* between the 64-bit kernel ABI and the 32-bit user ABI.
*/
static int
{
sizeof (vaparms32)))
return (EFAULT);
cnt = PC_VAPARMCNT;
}
return (0);
}
(get_udatamodel() == DATAMODEL_NATIVE ? \
#else
#endif
/*
* The priocntl system call.
*/
long
{
char *outstr;
int count;
int clnullflag;
int error = 0;
int error1 = 0;
int rv = 0;
int size;
/*
* First just check the version number. Right now there is only
* one version we know about and support. If we get some other
* version number from the application it may be that the
* application was built with some future version and is trying
* to run on an old release of the system (that's us). In any
* case if we don't recognize the version number all we can do is
* return error.
*/
if (pc_version != PC_VERSION)
if (seg == UIO_USERSPACE) {
} else {
}
switch (cmd) {
case PC_GETCID:
/*
* If the arg pointer is NULL, the user just wants to
* know the number of classes. If non-NULL, the pointer
* should point to a valid user pcinfo buffer. In the
* dynamic world we need to return the number of loaded
* classes, not the max number of available classes that
* can be loaded.
*/
rv = loaded_classes;
break;
} else {
}
/*
* Get the class ID corresponding to user supplied name.
*/
if (error)
/*
* Can't get info about the sys class.
*/
/*
* Get the class specific information.
* we MUST make sure that the class has not already
* been unloaded before we try the CL_GETCLINFO.
* If it has then we need to load it.
*/
error =
if (error)
if (error)
rv = loaded_classes;
break;
case PC_GETCLINFO:
/*
* If the arg pointer is NULL, the user just wants to know
* the number of classes. If non-NULL, the pointer should
* point to a valid user pcinfo buffer.
*/
rv = loaded_classes;
break;
} else {
}
/*
* Get the class specific information. we MUST make sure
* that the class has not already been unloaded before we
* try the CL_GETCLINFO. If it has then we need to load
* it.
*/
error =
if (error)
if (error)
rv = loaded_classes;
break;
case PC_SETPARMS:
case PC_SETXPARMS:
/*
* First check the validity of the parameters we got from
* the user. We don't do any permissions checking here
* because it's done on a per thread basis by parmsset().
*/
if (cmd == PC_SETPARMS) {
} else {
seg))
}
if (error)
/*
* Get the procset from the user.
*/
/*
* For performance we do a quick check here to catch
* common cases where the current thread is the only one
* in the set. In such cases we can call parmsset()
* directly, avoiding the relatively lengthy path through
* dotoprocs(). The underlying classes expect pidlock to
* be held.
*/
/* do a single LWP */
} else {
/* do the entire process otherwise */
}
if (error)
} else {
/*
* The dotoprocs() call below will cause
* setparms() to be called for each thread in the
* specified procset. setparms() will in turn
* call parmsset() (which does the real work).
*/
(char *)&stprmargs);
}
/*
* take care of the case when any of the
* operands happen to be LWP's
*/
(char *)&pcparms);
/*
* Dotolwp() returns with p_lock held.
* This is required for the GETPARMS case
* below. So, here we just release the
* p_lock.
*/
}
/*
* If setparms() encounters a permissions error
* for one or more of the threads it returns
* EPERM in stp_error so dotoprocs() will
* continue through the thread set. If
* dotoprocs() returned an error above, it was
* more serious than permissions and dotoprocs
* quit when the error was encountered. We
* return the more serious error if there was
* one, otherwise we return EPERM if we got that
* back.
*/
}
break;
case PC_GETPARMS:
case PC_GETXPARMS:
if (cmd == PC_GETPARMS) {
} else {
} else
seg))
}
/*
* Check to see if the current thread is the only one
* in the set. If not we must go through the whole set
* to select a thread.
*/
/* do a single LWP */
/*
* Specified thread not in
* specified class.
*/
} else {
}
} else {
count = 0;
retthreadp = NULL;
/*
* Specified thread not in specified class.
*/
if (error) {
if (retthreadp != NULL)
}
}
} else {
/*
* get initpp early to avoid lock ordering problems
* (we cannot get pidlock while holding any p_lock).
*/
/*
* Select the thread (from the set) whose
* parameters we are going to return. First we
* set up some locations for return values, then
* we call proccmp() indirectly through
* dotoprocs(). proccmp() will call a class
* specific routine which actually does the
* selection. To understand how this works take
* a careful look at the code below, the
* dotoprocs() function, the proccmp() function,
* and the class specific cl_proccmp() functions.
*/
clnullflag = 1;
else
clnullflag = 0;
count = 0;
retthreadp = NULL;
(char *)&pcmpargs);
}
/*
* take care of combination of LWP and process
* set case in a procset
*/
(char *)&pcmpargs);
}
/*
* Both proccmp() and threadcmp() return with the
* p_lock held for the ttoproc(retthreadp). This
* is required to make sure that the process we
* chose as the winner doesn't go away
* i.e. retthreadp has to be a valid pointer.
*
* The case below can only happen if the thread
* with the highest priority was not in your
* process. In that case, dotolwp will return
* holding p_lock for both your process as well
* as the process in which retthreadp is a
* thread.
*/
if ((retthreadp != NULL) &&
if (error) {
if (retthreadp != NULL)
/* CSTYLED */
}
/*
* dotoprocs() ignores the init process if it is
* in the set, unless it was the only process found.
* Since we are getting parameters here rather than
* setting them, we want to make sure init is not
* excluded if it is in the set.
*/
(retthreadp != NULL) &&
/*
* If dotoprocs returned success it found at least
* one thread in the set. If proccmp() failed to
* select a thread it is because the user specified
* a class and none of the threads in the set
* belonged to that class, or because the process
* specified was in the middle of exiting and had
* cleared its thread list.
*/
if (retthreadp == NULL) {
/*
* Might be here and still holding p_lock
* if we did a dotolwp on an lwp that
* existed but was in the wrong class.
*/
}
/*
* User can only use PC_CLNULL with one thread in set.
*/
if (retthreadp != NULL)
}
}
/*
* It is possible to have retthreadp == NULL. Proccmp()
* in the rare case (p_tlist == NULL) could return without
* setting a value for retthreadp.
*/
if (retthreadp == NULL) {
}
/*
* We've selected a thread so now get the parameters.
*/
/*
* Prepare to return parameters to the user
*/
/*
* Save pid of selected thread before dropping p_lock.
*/
if (error)
if (cmd == PC_GETPARMS) {
seg)) != 0)
/*
* And finally, return the pid of the selected thread.
*/
break;
case PC_ADMIN:
if (get_udatamodel() == DATAMODEL_NATIVE) {
#ifdef _SYSCALL32_IMPL
} else {
/* pcadmin struct from ILP32 callers */
#endif /* _SYSCALL32_IMPL */
}
/*
* Have the class do whatever the user is requesting.
*/
CRED());
mutex_exit(&ualock);
break;
case PC_GETPRIRANGE:
if (!error) {
}
break;
case PC_DONICE:
/*
* Get pcnice and procset structures from the user.
*/
}
break;
case PC_DOPRIO:
/*
* Get pcprio and procset structures from the user.
*/
}
break;
case PC_SETDFLCL:
if (secpolicy_dispadm(CRED()) != 0)
break;
case PC_GETDFLCL:
if (defaultcid >= loaded_classes)
outstr = "";
else
break;
default:
break;
}
}
long
{
}
/*
* The proccmp() function is part of the implementation of the
* PC_GETPARMS command of the priocntl system call. This function works
* with the system call code and with the class specific cl_globpri()
* function to select one thread from a specified procset based on class
* specific criteria. proccmp() is called indirectly from the priocntl
* code through the dotoprocs function. Basic strategy is dotoprocs()
* calls us once for each thread in the set. We in turn call the class
* specific function to compare the current thread from dotoprocs to the
* "best" (according to the class criteria) found so far. We keep the
* "best" thread in *pcmp_retthreadp.
*/
static int
{
int last_pri = -1;
int tx_pri;
int found = 0;
return (0);
}
/*
* If no cid is specified, then lets just pick the first one.
* It doesn't matter because if the number of processes in the
* set are more than 1, then we return EINVAL in priocntlsys.
*/
}
do {
/*
* We found one which matches the required cid.
*/
found = 1;
}
}
if (found) {
/*
* First time through for this set.
* keep the mutex held. He might be the one!
*/
} else {
} else {
}
}
} else {
/*
* We actually didn't find anything of the same cid in
* this process.
*/
}
return (0);
}
int
{
/*
* If no cid is specified, then lets just pick the first one.
* It doesn't matter because if the number of threads in the
* set are more than 1, then we return EINVAL in priocntlsys.
*/
}
/*
* First time through for this set.
*/
} else {
/*
* Unlike proccmp(), we don't release the
* p_lock of the ttoproc(tp) if tp's global
* priority is less than tx's. We need to go
* through the entire list before we can do
* that. The p_lock is released by the caller
* of dotolwp().
*/
}
}
}
}
return (0);
}
/*
* The setparms() function is called indirectly by priocntlsys()
* through the dotoprocs() function. setparms() acts as an
* intermediary between dotoprocs() and the parmsset() function,
* calling parmsset() for each thread in the set and handling
* the error returns on their way back up to dotoprocs().
*/
static int
{
int error = 0;
kthread_t *t;
int err;
return (0);
}
do {
if (error == 0)
if (error) {
return (0);
} else {
return (error);
}
} else
return (0);
}
int
{
int error;
int nice;
int inc;
/*
* The XPG5 standard requires that any realtime process or thread
* must be unaffected by a call to setpriority().
*/
return (0);
}
return (error);
/*
* If there is no change to priority, we should return the
* highest priority (lowest numerical value) pertaining to
* any of the specified threads.
*/
} else {
/*
* Try to change the nice value of the thread.
*/
}
return (error);
}
int
{
int retval = 0;
int error;
return (ESRCH);
}
/*
* Check permissions before changing the nice value.
*/
return (EPERM);
}
}
do {
if (error)
return (retval);
}
/*
* Update the nice value of the specified LWP or set of processes.
*/
static int
{
int err_proc = 0;
int err_thread = 0;
int err = 0;
/*
* Sanity check.
*/
return (EINVAL);
/*
* If it is PC_GETNICE operation then set pc_val to the largest
* possible nice value to help us find the lowest nice value
* pertaining to any of the specified processes.
*/
/*
* dotolwp() can return with p_lock held. This is required
* for the priocntl GETPARMS case. So, here we just release
* the p_lock.
*/
/*
* If we were called for a single LWP, then ignore ESRCH
* returned by the previous dotoprocs() call.
*/
err_proc = 0;
}
/*
* dotoprocs() ignores the init process if it is in the set, unless
* it was the only process found. We want to make sure init is not
* excluded if we're going PC_GETNICE operation.
*/
}
/*
* We're returning the latest error here that we've got back from
* err_proc can be replaced by err.
*/
if (!err)
return (err);
}
int
{
int prio = 0;
int incr;
int error;
/*
* Target thread must change to new class.
* See comments in parmsset(), from where this code was copied.
*/
if (error)
return (error);
return (ENOMEM);
if (error) {
return (error);
}
}
return (error);
/*
* If we are not setting the priority, we should return the
* highest priority pertaining to any of the specified threads.
*/
}
/*
* Try to change the priority of the thread.
*/
}
return (error);
}
int
{
int retval = 0;
int error;
return (ESRCH);
}
/*
* Check permissions before changing the prio value.
*/
return (EPERM);
}
}
do {
if (error)
return (retval);
}
/*
* Set the class and priority of the specified LWP or set of processes.
*/
static int
{
int err_proc = 0;
int err_thread = 0;
int err = 0;
/*
* Sanity check.
*/
return (EINVAL);
return (EINVAL);
/*
* If it is a PC_GETPRIO operation then set pc_val to the smallest
* possible prio value to help us find the highest priority
* pertaining to any of the specified processes.
*/
/*
* dotolwp() can return with p_lock held. This is required
* for the priocntl GETPARMS case. So, here we just release
* the p_lock.
*/
/*
* If we were called for a single LWP, then ignore ESRCH
* returned by the previous dotoprocs() call.
*/
err_proc = 0;
}
/*
* dotoprocs() ignores the init process if it is in the set, unless
* it was the only process found. We want to make sure init is not
* excluded if we're going PC_GETPRIO operation.
*/
}
/*
* We're returning the latest error here that we've got back from
* err_proc can be replaced by err.
*/
if (!err)
return (err);
}