class.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* 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
* 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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/cmn_err.h>
#include <sys/class.h>
#include <sys/kmem.h>
#include <sys/cred.h>
#include <sys/proc.h>
#include <sys/procset.h>
#include <sys/modctl.h>
#include <sys/disp.h>
#include <sys/sysmacros.h>
static int getcidbyname_locked(char *, id_t *);
/*
* Allocate a cid given a class name if one is not already allocated.
* Returns 0 if the cid was already exists or if the allocation of a new
* cid was successful. Nonzero return indicates error.
*/
int
alloc_cid(char *clname, id_t *cidp)
{
sclass_t *clp;
ASSERT(MUTEX_HELD(&class_lock));
/*
* If the clname doesn't already have a cid, allocate one.
*/
if (getcidbyname_locked(clname, cidp) != 0) {
/*
* Allocate a class entry and a lock for it.
*/
for (clp = sclass; clp < &sclass[nclass]; clp++)
if (clp->cl_name[0] == '\0' && clp->cl_lock == NULL)
break;
if (clp == &sclass[nclass]) {
return (ENOSPC);
}
*cidp = clp - &sclass[0];
clp->cl_lock = kmem_alloc(sizeof (krwlock_t), KM_SLEEP);
clp->cl_name = kmem_alloc(strlen(clname) + 1, KM_SLEEP);
(void) strcpy(clp->cl_name, clname);
rw_init(clp->cl_lock, NULL, RW_DEFAULT, NULL);
}
/*
* At this point, *cidp will contain the index into the class
* array for the given class name.
*/
return (0);
}
int
scheduler_load(char *clname, sclass_t *clp)
{
int rv = 0;
if (LOADABLE_SCHED(clp)) {
rw_enter(clp->cl_lock, RW_READER);
if (!SCHED_INSTALLED(clp)) {
rw_exit(clp->cl_lock);
if (modload("sched", clname) == -1)
return (EINVAL);
rw_enter(clp->cl_lock, RW_READER);
/* did we really load a scheduling class? */
if (!SCHED_INSTALLED(clp))
rv = EINVAL;
}
rw_exit(clp->cl_lock);
}
return (rv);
}
/*
* Get class ID given class name.
*/
int
getcid(char *clname, id_t *cidp)
{
sclass_t *clp;
int retval;
mutex_enter(&class_lock);
if ((retval = alloc_cid(clname, cidp)) == 0) {
clp = &sclass[*cidp];
clp->cl_count++;
/*
* If it returns zero, it's loaded & locked
* or we found a statically installed scheduler
* module.
* If it returns EINVAL, modload() failed when
* it tried to load the module.
*/
mutex_exit(&class_lock);
retval = scheduler_load(clname, clp);
mutex_enter(&class_lock);
clp->cl_count--;
if (retval != 0 && clp->cl_count == 0) {
/* last guy out of scheduler_load frees the storage */
kmem_free(clp->cl_name, strlen(clname) + 1);
kmem_free(clp->cl_lock, sizeof (krwlock_t));
clp->cl_name = "";
clp->cl_lock = (krwlock_t *)NULL;
}
}
mutex_exit(&class_lock);
return (retval);
}
static int
getcidbyname_locked(char *clname, id_t *cidp)
{
sclass_t *clp;
ASSERT(MUTEX_HELD(&class_lock));
if (*clname == NULL)
return (EINVAL);
for (clp = &sclass[0]; clp < &sclass[nclass]; clp++) {
if (strcmp(clp->cl_name, clname) == 0) {
*cidp = clp - &sclass[0];
return (0);
}
}
return (EINVAL);
}
/*
* Lookup a module by name.
*/
int
getcidbyname(char *clname, id_t *cidp)
{
int retval;
mutex_enter(&class_lock);
retval = getcidbyname_locked(clname, cidp);
mutex_exit(&class_lock);
return (retval);
}
/*
* Get the scheduling parameters of the thread pointed to by
* tp into the buffer pointed to by parmsp.
*/
void
parmsget(kthread_id_t tp, pcparms_t *parmsp)
{
parmsp->pc_cid = tp->t_cid;
CL_PARMSGET(tp, parmsp->pc_clparms);
}
/*
* Check the validity of the scheduling parameters in the buffer
* pointed to by parmsp.
* Note that the format of the parameters may be changed by class
* specific code which we call.
*/
int
parmsin(pcparms_t *parmsp, pc_vaparms_t *vaparmsp)
{
if (parmsp->pc_cid >= loaded_classes || parmsp->pc_cid < 1)
return (EINVAL);
/*
* Call the class specific routine to validate class
* specific parameters.
* The input parameters are either in a pcparms structure (PC_SETPARMS)
* or in a variable parameter structure (PC_SETXPARMS). In the
* 'PC_SETPARMS' case vaparmsp is a NULL pointer and a CL_PARMSIN()
* routine gets the parameter. Otherwise vaparmsp points to a variable
* parameter structure and a CL_VAPARMSIN() routine gets the parameter.
*/
if (vaparmsp != NULL)
return (CL_VAPARMSIN(&sclass[parmsp->pc_cid],
parmsp->pc_clparms, vaparmsp));
else
return (CL_PARMSIN(&sclass[parmsp->pc_cid],
parmsp->pc_clparms));
}
/*
* Call the class specific code to do the required processing
* before the scheduling parameters are copied out to the user.
* Note that the format of the parameters may be changed by the
* class specific code.
*/
int
parmsout(pcparms_t *parmsp, pc_vaparms_t *vaparmsp)
{
return (CL_PARMSOUT(&sclass[parmsp->pc_cid], parmsp->pc_clparms,
vaparmsp));
}
/*
* Set the scheduling parameters of the thread pointed to by
* targtp to those specified in the pcparms structure pointed
* to by parmsp. If reqtp is non-NULL it points to the thread
* that initiated the request for the parameter change and indicates
* that our caller wants us to verify that the requesting thread
* has the appropriate permissions.
*/
int
parmsset(pcparms_t *parmsp, kthread_id_t targtp)
{
caddr_t clprocp;
int error;
cred_t *reqpcredp;
proc_t *reqpp = ttoproc(curthread);
proc_t *targpp = ttoproc(targtp);
id_t oldcid;
ASSERT(MUTEX_HELD(&pidlock));
ASSERT(MUTEX_HELD(&targpp->p_lock));
if (reqpp != NULL) {
mutex_enter(&reqpp->p_crlock);
crhold(reqpcredp = reqpp->p_cred);
mutex_exit(&reqpp->p_crlock);
/*
* Check basic permissions.
*/
if (!prochasprocperm(targpp, reqpp, reqpcredp)) {
crfree(reqpcredp);
return (EPERM);
}
} else {
reqpcredp = NULL;
}
if (parmsp->pc_cid != targtp->t_cid) {
void *bufp = NULL;
/*
* Target thread must change to new class.
*/
clprocp = (caddr_t)targtp->t_cldata;
oldcid = targtp->t_cid;
/*
* Purpose: allow scheduling class to veto moves
* to other classes. All the classes, except FSS,
* do nothing except returning 0.
*/
error = CL_CANEXIT(targtp, reqpcredp);
if (error) {
/*
* Not allowed to leave the class, so return error.
*/
crfree(reqpcredp);
return (error);
} else {
/*
* Pre-allocate scheduling class data.
*/
if (CL_ALLOC(&bufp, parmsp->pc_cid, KM_NOSLEEP) != 0) {
error = ENOMEM; /* no memory available */
crfree(reqpcredp);
return (error);
} else {
error = CL_ENTERCLASS(targtp, parmsp->pc_cid,
parmsp->pc_clparms, reqpcredp, bufp);
crfree(reqpcredp);
if (error) {
CL_FREE(parmsp->pc_cid, bufp);
return (error);
}
}
}
CL_EXITCLASS(oldcid, clprocp);
} else {
/*
* Not changing class
*/
error = CL_PARMSSET(targtp, parmsp->pc_clparms,
curthread->t_cid, reqpcredp);
crfree(reqpcredp);
if (error)
return (error);
}
return (0);
}
/*
* Copy all selected class parameters to the user.
* The parameters are specified by a key.
*/
int
vaparmsout(char *classp, pcparms_t *prmsp, pc_vaparms_t *vaparmsp)
{
char *clname;
ASSERT(MUTEX_NOT_HELD(&curproc->p_lock));
if (classp != NULL)
return (CL_VAPARMSOUT(&sclass[prmsp->pc_cid],
prmsp->pc_clparms, vaparmsp));
switch (vaparmsp->pc_vaparmscnt) {
case 0:
return (0);
case 1:
break;
default:
return (EINVAL);
}
if (vaparmsp->pc_parms[0].pc_key != PC_KY_CLNAME)
return (EINVAL);
clname = sclass[prmsp->pc_cid].cl_name;
if (copyout(clname, (void *)(uintptr_t)vaparmsp->pc_parms[0].pc_parm,
MIN(strlen(clname) + 1, PC_CLNMSZ)))
return (EFAULT);
return (0);
}