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