Home | History | Annotate | Download | only in disp
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 #include <sys/types.h>
     30 #include <sys/systm.h>
     31 #include <sys/cmn_err.h>
     32 #include <sys/class.h>
     33 #include <sys/kmem.h>
     34 #include <sys/cred.h>
     35 #include <sys/proc.h>
     36 #include <sys/procset.h>
     37 #include <sys/modctl.h>
     38 #include <sys/disp.h>
     39 #include <sys/sysmacros.h>
     40 #include <sys/schedctl.h>
     41 
     42 static int getcidbyname_locked(char *, id_t *);
     43 
     44 /*
     45  * Allocate a cid given a class name if one is not already allocated.
     46  * Returns 0 if the cid was already exists or if the allocation of a new
     47  * cid was successful. Nonzero return indicates error.
     48  */
     49 int
     50 alloc_cid(char *clname, id_t *cidp)
     51 {
     52 	sclass_t *clp;
     53 
     54 	ASSERT(MUTEX_HELD(&class_lock));
     55 
     56 	/*
     57 	 * If the clname doesn't already have a cid, allocate one.
     58 	 */
     59 	if (getcidbyname_locked(clname, cidp) != 0) {
     60 		/*
     61 		 * Allocate a class entry and a lock for it.
     62 		 */
     63 		for (clp = sclass; clp < &sclass[nclass]; clp++)
     64 			if (clp->cl_name[0] == '\0' && clp->cl_lock == NULL)
     65 				break;
     66 
     67 		if (clp == &sclass[nclass]) {
     68 			return (ENOSPC);
     69 		}
     70 		*cidp = clp - &sclass[0];
     71 		clp->cl_lock = kmem_alloc(sizeof (krwlock_t), KM_SLEEP);
     72 		clp->cl_name = kmem_alloc(strlen(clname) + 1, KM_SLEEP);
     73 		(void) strcpy(clp->cl_name, clname);
     74 		rw_init(clp->cl_lock, NULL, RW_DEFAULT, NULL);
     75 	}
     76 
     77 	/*
     78 	 * At this point, *cidp will contain the index into the class
     79 	 * array for the given class name.
     80 	 */
     81 	return (0);
     82 }
     83 
     84 int
     85 scheduler_load(char *clname, sclass_t *clp)
     86 {
     87 	int rv = 0;
     88 
     89 	if (LOADABLE_SCHED(clp)) {
     90 		rw_enter(clp->cl_lock, RW_READER);
     91 		if (!SCHED_INSTALLED(clp)) {
     92 			rw_exit(clp->cl_lock);
     93 			if (modload("sched", clname) == -1)
     94 				return (EINVAL);
     95 			rw_enter(clp->cl_lock, RW_READER);
     96 			/* did we really load a scheduling class? */
     97 			if (!SCHED_INSTALLED(clp))
     98 				rv = EINVAL;
     99 		}
    100 		rw_exit(clp->cl_lock);
    101 	}
    102 	return (rv);
    103 }
    104 
    105 /*
    106  * Get class ID given class name.
    107  */
    108 int
    109 getcid(char *clname, id_t *cidp)
    110 {
    111 	sclass_t *clp;
    112 	int retval;
    113 
    114 	mutex_enter(&class_lock);
    115 	if ((retval = alloc_cid(clname, cidp)) == 0) {
    116 		clp = &sclass[*cidp];
    117 		clp->cl_count++;
    118 
    119 		/*
    120 		 * If it returns zero, it's loaded & locked
    121 		 * or we found a statically installed scheduler
    122 		 * module.
    123 		 * If it returns EINVAL, modload() failed when
    124 		 * it tried to load the module.
    125 		 */
    126 		mutex_exit(&class_lock);
    127 		retval = scheduler_load(clname, clp);
    128 		mutex_enter(&class_lock);
    129 
    130 		clp->cl_count--;
    131 		if (retval != 0 && clp->cl_count == 0) {
    132 			/* last guy out of scheduler_load frees the storage */
    133 			kmem_free(clp->cl_name, strlen(clname) + 1);
    134 			kmem_free(clp->cl_lock, sizeof (krwlock_t));
    135 			clp->cl_name = "";
    136 			clp->cl_lock = (krwlock_t *)NULL;
    137 		}
    138 	}
    139 	mutex_exit(&class_lock);
    140 	return (retval);
    141 
    142 }
    143 
    144 static int
    145 getcidbyname_locked(char *clname, id_t *cidp)
    146 {
    147 	sclass_t *clp;
    148 
    149 	ASSERT(MUTEX_HELD(&class_lock));
    150 
    151 	if (*clname == NULL)
    152 		return (EINVAL);
    153 
    154 	for (clp = &sclass[0]; clp < &sclass[nclass]; clp++) {
    155 		if (strcmp(clp->cl_name, clname) == 0) {
    156 			*cidp = clp - &sclass[0];
    157 			return (0);
    158 		}
    159 	}
    160 	return (EINVAL);
    161 }
    162 
    163 /*
    164  * Lookup a module by name.
    165  */
    166 int
    167 getcidbyname(char *clname, id_t *cidp)
    168 {
    169 	int retval;
    170 
    171 	mutex_enter(&class_lock);
    172 	retval = getcidbyname_locked(clname, cidp);
    173 	mutex_exit(&class_lock);
    174 
    175 	return (retval);
    176 }
    177 
    178 /*
    179  * Get the scheduling parameters of the thread pointed to by
    180  * tp into the buffer pointed to by parmsp.
    181  */
    182 void
    183 parmsget(kthread_t *tp, pcparms_t *parmsp)
    184 {
    185 	parmsp->pc_cid = tp->t_cid;
    186 	CL_PARMSGET(tp, parmsp->pc_clparms);
    187 }
    188 
    189 
    190 /*
    191  * Check the validity of the scheduling parameters in the buffer
    192  * pointed to by parmsp.
    193  * Note that the format of the parameters may be changed by class
    194  * specific code which we call.
    195  */
    196 int
    197 parmsin(pcparms_t *parmsp, pc_vaparms_t *vaparmsp)
    198 {
    199 	if (parmsp->pc_cid >= loaded_classes || parmsp->pc_cid < 1)
    200 		return (EINVAL);
    201 
    202 	/*
    203 	 * Call the class specific routine to validate class
    204 	 * specific parameters.
    205 	 * The input parameters are either in a pcparms structure (PC_SETPARMS)
    206 	 * or in a variable parameter structure (PC_SETXPARMS). In the
    207 	 * 'PC_SETPARMS' case vaparmsp is a NULL pointer and a CL_PARMSIN()
    208 	 * routine gets the parameter. Otherwise vaparmsp points to a variable
    209 	 * parameter structure and a CL_VAPARMSIN() routine gets the parameter.
    210 	 */
    211 	if (vaparmsp != NULL)
    212 		return (CL_VAPARMSIN(&sclass[parmsp->pc_cid],
    213 		    parmsp->pc_clparms, vaparmsp));
    214 	else
    215 		return (CL_PARMSIN(&sclass[parmsp->pc_cid],
    216 		    parmsp->pc_clparms));
    217 }
    218 
    219 
    220 /*
    221  * Call the class specific code to do the required processing
    222  * before the scheduling parameters are copied out to the user.
    223  * Note that the format of the parameters may be changed by the
    224  * class specific code.
    225  */
    226 int
    227 parmsout(pcparms_t *parmsp, pc_vaparms_t *vaparmsp)
    228 {
    229 	return (CL_PARMSOUT(&sclass[parmsp->pc_cid], parmsp->pc_clparms,
    230 	    vaparmsp));
    231 }
    232 
    233 
    234 /*
    235  * Set the scheduling parameters of the thread pointed to by
    236  * targtp to those specified in the pcparms structure pointed
    237  * to by parmsp.  If reqtp is non-NULL it points to the thread
    238  * that initiated the request for the parameter change and indicates
    239  * that our caller wants us to verify that the requesting thread
    240  * has the appropriate permissions.
    241  */
    242 int
    243 parmsset(pcparms_t *parmsp, kthread_t *targtp)
    244 {
    245 	caddr_t	clprocp;
    246 	int	error;
    247 	cred_t	*reqpcredp;
    248 	proc_t	*reqpp = ttoproc(curthread);
    249 	proc_t	*targpp = ttoproc(targtp);
    250 	id_t	oldcid;
    251 
    252 	ASSERT(MUTEX_HELD(&pidlock));
    253 	ASSERT(MUTEX_HELD(&targpp->p_lock));
    254 	if (reqpp != NULL) {
    255 		mutex_enter(&reqpp->p_crlock);
    256 		crhold(reqpcredp = reqpp->p_cred);
    257 		mutex_exit(&reqpp->p_crlock);
    258 
    259 		/*
    260 		 * Check basic permissions.
    261 		 */
    262 		if (!prochasprocperm(targpp, reqpp, reqpcredp)) {
    263 			crfree(reqpcredp);
    264 			return (EPERM);
    265 		}
    266 	} else {
    267 		reqpcredp = NULL;
    268 	}
    269 
    270 	if (parmsp->pc_cid != targtp->t_cid) {
    271 		void	*bufp = NULL;
    272 		/*
    273 		 * Target thread must change to new class.
    274 		 */
    275 		clprocp = (caddr_t)targtp->t_cldata;
    276 		oldcid  = targtp->t_cid;
    277 
    278 		/*
    279 		 * Purpose: allow scheduling class to veto moves
    280 		 * to other classes. All the classes, except FSS,
    281 		 * do nothing except returning 0.
    282 		 */
    283 		error = CL_CANEXIT(targtp, reqpcredp);
    284 		if (error) {
    285 			/*
    286 			 * Not allowed to leave the class, so return error.
    287 			 */
    288 			crfree(reqpcredp);
    289 			return (error);
    290 		} else {
    291 			/*
    292 			 * Pre-allocate scheduling class data.
    293 			 */
    294 			if (CL_ALLOC(&bufp, parmsp->pc_cid, KM_NOSLEEP) != 0) {
    295 				error = ENOMEM; /* no memory available */
    296 				crfree(reqpcredp);
    297 				return (error);
    298 			} else {
    299 				error = CL_ENTERCLASS(targtp, parmsp->pc_cid,
    300 				    parmsp->pc_clparms, reqpcredp, bufp);
    301 				crfree(reqpcredp);
    302 				if (error) {
    303 					CL_FREE(parmsp->pc_cid, bufp);
    304 					return (error);
    305 				}
    306 			}
    307 		}
    308 		CL_EXITCLASS(oldcid, clprocp);
    309 	} else {
    310 
    311 		/*
    312 		 * Not changing class
    313 		 */
    314 		error = CL_PARMSSET(targtp, parmsp->pc_clparms,
    315 		    curthread->t_cid, reqpcredp);
    316 		crfree(reqpcredp);
    317 		if (error)
    318 			return (error);
    319 	}
    320 	schedctl_set_cidpri(targtp);
    321 	return (0);
    322 }
    323 
    324 
    325 /*
    326  * Copy all selected class parameters to the user.
    327  * The parameters are specified by a key.
    328  */
    329 int
    330 vaparmsout(char *classp, pcparms_t *prmsp, pc_vaparms_t *vaparmsp,
    331     uio_seg_t seg)
    332 {
    333 	char	*clname;
    334 
    335 	ASSERT(MUTEX_NOT_HELD(&curproc->p_lock));
    336 
    337 	if (classp != NULL)
    338 		return (CL_VAPARMSOUT(&sclass[prmsp->pc_cid],
    339 		    prmsp->pc_clparms, vaparmsp));
    340 
    341 	switch (vaparmsp->pc_vaparmscnt) {
    342 	case 0:
    343 		return (0);
    344 	case 1:
    345 		break;
    346 	default:
    347 		return (EINVAL);
    348 	}
    349 
    350 	if (vaparmsp->pc_parms[0].pc_key != PC_KY_CLNAME)
    351 		return (EINVAL);
    352 
    353 	clname = sclass[prmsp->pc_cid].cl_name;
    354 	if ((seg == UIO_USERSPACE ? copyout : kcopy)(clname,
    355 	    (void *)(uintptr_t)vaparmsp->pc_parms[0].pc_parm,
    356 	    MIN(strlen(clname) + 1, PC_CLNMSZ)))
    357 		return (EFAULT);
    358 
    359 	return (0);
    360 }
    361