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 	char *tmp = clname + 1;
     89 
     90 	/* Check if class name is  "",  ".",  ".."  or  "`"  */
     91 	if (*clname == '\0' || *clname == '`' || (*clname == '.' && *tmp == '\0') ||
     92 	    (*clname == '.' && *tmp == '.' && *(++tmp) == '\0'))
     93 		return (EINVAL);
     94 
     95 	if (LOADABLE_SCHED(clp)) {
     96 		rw_enter(clp->cl_lock, RW_READER);
     97 		if (!SCHED_INSTALLED(clp)) {
     98 			rw_exit(clp->cl_lock);
     99 			if (modload("sched", clname) == -1)
    100 				return (EINVAL);
    101 			rw_enter(clp->cl_lock, RW_READER);
    102 			/* did we really load a scheduling class? */
    103 			if (!SCHED_INSTALLED(clp))
    104 				rv = EINVAL;
    105 		}
    106 		rw_exit(clp->cl_lock);
    107 	}
    108 	return (rv);
    109 }
    110 
    111 /*
    112  * Get class ID given class name.
    113  */
    114 int
    115 getcid(char *clname, id_t *cidp)
    116 {
    117 	sclass_t *clp;
    118 	int retval;
    119 
    120 	mutex_enter(&class_lock);
    121 	if ((retval = alloc_cid(clname, cidp)) == 0) {
    122 		clp = &sclass[*cidp];
    123 		clp->cl_count++;
    124 
    125 		/*
    126 		 * If it returns zero, it's loaded & locked
    127 		 * or we found a statically installed scheduler
    128 		 * module.
    129 		 * If it returns EINVAL, modload() failed when
    130 		 * it tried to load the module.
    131 		 */
    132 		mutex_exit(&class_lock);
    133 		retval = scheduler_load(clname, clp);
    134 		mutex_enter(&class_lock);
    135 
    136 		clp->cl_count--;
    137 		if (retval != 0 && clp->cl_count == 0) {
    138 			/* last guy out of scheduler_load frees the storage */
    139 			kmem_free(clp->cl_name, strlen(clname) + 1);
    140 			kmem_free(clp->cl_lock, sizeof (krwlock_t));
    141 			clp->cl_name = "";
    142 			clp->cl_lock = (krwlock_t *)NULL;
    143 		}
    144 	}
    145 	mutex_exit(&class_lock);
    146 	return (retval);
    147 
    148 }
    149 
    150 static int
    151 getcidbyname_locked(char *clname, id_t *cidp)
    152 {
    153 	sclass_t *clp;
    154 
    155 	ASSERT(MUTEX_HELD(&class_lock));
    156 
    157 	if (*clname == NULL)
    158 		return (EINVAL);
    159 
    160 	for (clp = &sclass[0]; clp < &sclass[nclass]; clp++) {
    161 		if (strcmp(clp->cl_name, clname) == 0) {
    162 			*cidp = clp - &sclass[0];
    163 			return (0);
    164 		}
    165 	}
    166 	return (EINVAL);
    167 }
    168 
    169 /*
    170  * Lookup a module by name.
    171  */
    172 int
    173 getcidbyname(char *clname, id_t *cidp)
    174 {
    175 	int retval;
    176 
    177 	mutex_enter(&class_lock);
    178 	retval = getcidbyname_locked(clname, cidp);
    179 	mutex_exit(&class_lock);
    180 
    181 	return (retval);
    182 }
    183 
    184 /*
    185  * Get the scheduling parameters of the thread pointed to by
    186  * tp into the buffer pointed to by parmsp.
    187  */
    188 void
    189 parmsget(kthread_t *tp, pcparms_t *parmsp)
    190 {
    191 	parmsp->pc_cid = tp->t_cid;
    192 	CL_PARMSGET(tp, parmsp->pc_clparms);
    193 }
    194 
    195 
    196 /*
    197  * Check the validity of the scheduling parameters in the buffer
    198  * pointed to by parmsp.
    199  * Note that the format of the parameters may be changed by class
    200  * specific code which we call.
    201  */
    202 int
    203 parmsin(pcparms_t *parmsp, pc_vaparms_t *vaparmsp)
    204 {
    205 	if (parmsp->pc_cid >= loaded_classes || parmsp->pc_cid < 1)
    206 		return (EINVAL);
    207 
    208 	/*
    209 	 * Call the class specific routine to validate class
    210 	 * specific parameters.
    211 	 * The input parameters are either in a pcparms structure (PC_SETPARMS)
    212 	 * or in a variable parameter structure (PC_SETXPARMS). In the
    213 	 * 'PC_SETPARMS' case vaparmsp is a NULL pointer and a CL_PARMSIN()
    214 	 * routine gets the parameter. Otherwise vaparmsp points to a variable
    215 	 * parameter structure and a CL_VAPARMSIN() routine gets the parameter.
    216 	 */
    217 	if (vaparmsp != NULL)
    218 		return (CL_VAPARMSIN(&sclass[parmsp->pc_cid],
    219 		    parmsp->pc_clparms, vaparmsp));
    220 	else
    221 		return (CL_PARMSIN(&sclass[parmsp->pc_cid],
    222 		    parmsp->pc_clparms));
    223 }
    224 
    225 
    226 /*
    227  * Call the class specific code to do the required processing
    228  * before the scheduling parameters are copied out to the user.
    229  * Note that the format of the parameters may be changed by the
    230  * class specific code.
    231  */
    232 int
    233 parmsout(pcparms_t *parmsp, pc_vaparms_t *vaparmsp)
    234 {
    235 	return (CL_PARMSOUT(&sclass[parmsp->pc_cid], parmsp->pc_clparms,
    236 	    vaparmsp));
    237 }
    238 
    239 
    240 /*
    241  * Set the scheduling parameters of the thread pointed to by
    242  * targtp to those specified in the pcparms structure pointed
    243  * to by parmsp.  If reqtp is non-NULL it points to the thread
    244  * that initiated the request for the parameter change and indicates
    245  * that our caller wants us to verify that the requesting thread
    246  * has the appropriate permissions.
    247  */
    248 int
    249 parmsset(pcparms_t *parmsp, kthread_t *targtp)
    250 {
    251 	caddr_t	clprocp;
    252 	int	error;
    253 	cred_t	*reqpcredp;
    254 	proc_t	*reqpp = ttoproc(curthread);
    255 	proc_t	*targpp = ttoproc(targtp);
    256 	id_t	oldcid;
    257 
    258 	ASSERT(MUTEX_HELD(&pidlock));
    259 	ASSERT(MUTEX_HELD(&targpp->p_lock));
    260 	if (reqpp != NULL) {
    261 		mutex_enter(&reqpp->p_crlock);
    262 		crhold(reqpcredp = reqpp->p_cred);
    263 		mutex_exit(&reqpp->p_crlock);
    264 
    265 		/*
    266 		 * Check basic permissions.
    267 		 */
    268 		if (!prochasprocperm(targpp, reqpp, reqpcredp)) {
    269 			crfree(reqpcredp);
    270 			return (EPERM);
    271 		}
    272 	} else {
    273 		reqpcredp = NULL;
    274 	}
    275 
    276 	if (parmsp->pc_cid != targtp->t_cid) {
    277 		void	*bufp = NULL;
    278 		/*
    279 		 * Target thread must change to new class.
    280 		 */
    281 		clprocp = (caddr_t)targtp->t_cldata;
    282 		oldcid  = targtp->t_cid;
    283 
    284 		/*
    285 		 * Purpose: allow scheduling class to veto moves
    286 		 * to other classes. All the classes, except FSS,
    287 		 * do nothing except returning 0.
    288 		 */
    289 		error = CL_CANEXIT(targtp, reqpcredp);
    290 		if (error) {
    291 			/*
    292 			 * Not allowed to leave the class, so return error.
    293 			 */
    294 			crfree(reqpcredp);
    295 			return (error);
    296 		} else {
    297 			/*
    298 			 * Pre-allocate scheduling class data.
    299 			 */
    300 			if (CL_ALLOC(&bufp, parmsp->pc_cid, KM_NOSLEEP) != 0) {
    301 				error = ENOMEM; /* no memory available */
    302 				crfree(reqpcredp);
    303 				return (error);
    304 			} else {
    305 				error = CL_ENTERCLASS(targtp, parmsp->pc_cid,
    306 				    parmsp->pc_clparms, reqpcredp, bufp);
    307 				crfree(reqpcredp);
    308 				if (error) {
    309 					CL_FREE(parmsp->pc_cid, bufp);
    310 					return (error);
    311 				}
    312 			}
    313 		}
    314 		CL_EXITCLASS(oldcid, clprocp);
    315 	} else {
    316 
    317 		/*
    318 		 * Not changing class
    319 		 */
    320 		error = CL_PARMSSET(targtp, parmsp->pc_clparms,
    321 		    curthread->t_cid, reqpcredp);
    322 		crfree(reqpcredp);
    323 		if (error)
    324 			return (error);
    325 	}
    326 	schedctl_set_cidpri(targtp);
    327 	return (0);
    328 }
    329 
    330 
    331 /*
    332  * Copy all selected class parameters to the user.
    333  * The parameters are specified by a key.
    334  */
    335 int
    336 vaparmsout(char *classp, pcparms_t *prmsp, pc_vaparms_t *vaparmsp,
    337     uio_seg_t seg)
    338 {
    339 	char	*clname;
    340 
    341 	ASSERT(MUTEX_NOT_HELD(&curproc->p_lock));
    342 
    343 	if (classp != NULL)
    344 		return (CL_VAPARMSOUT(&sclass[prmsp->pc_cid],
    345 		    prmsp->pc_clparms, vaparmsp));
    346 
    347 	switch (vaparmsp->pc_vaparmscnt) {
    348 	case 0:
    349 		return (0);
    350 	case 1:
    351 		break;
    352 	default:
    353 		return (EINVAL);
    354 	}
    355 
    356 	if (vaparmsp->pc_parms[0].pc_key != PC_KY_CLNAME)
    357 		return (EINVAL);
    358 
    359 	clname = sclass[prmsp->pc_cid].cl_name;
    360 	if ((seg == UIO_USERSPACE ? copyout : kcopy)(clname,
    361 	    (void *)(uintptr_t)vaparmsp->pc_parms[0].pc_parm,
    362 	    MIN(strlen(clname) + 1, PC_CLNMSZ)))
    363 		return (EFAULT);
    364 
    365 	return (0);
    366 }
    367