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