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