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