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