/* * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include 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; char *tmp = clname + 1; /* Check if class name is "", ".", ".." or "`" */ if (*clname == '\0' || *clname == '`' || (*clname == '.' && *tmp == '\0') || (*clname == '.' && *tmp == '.' && *(++tmp) == '\0')) return (EINVAL); 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 == '\0') 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_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_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); } schedctl_set_cidpri(targtp); 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, uio_seg_t seg) { 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 ((seg == UIO_USERSPACE ? copyout : kcopy)(clname, (void *)(uintptr_t)vaparmsp->pc_parms[0].pc_parm, MIN(strlen(clname) + 1, PC_CLNMSZ))) return (EFAULT); return (0); }