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