1 /* 2 * Copyright (c) 2005 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Sergey Glushchenko <deen@smz.com.ua> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 */ 35 36 #include <sys/errno.h> 37 #include <sys/globaldata.h> /* curthread */ 38 #include <sys/proc.h> 39 #include <sys/priv.h> 40 #include <sys/sysproto.h> /* struct usched_set_args */ 41 #include <sys/systm.h> /* strcmp() */ 42 #include <sys/usched.h> 43 44 #include <sys/mplock2.h> 45 46 #include <machine/cpumask.h> 47 #include <machine/smp.h> 48 49 static TAILQ_HEAD(, usched) usched_list = TAILQ_HEAD_INITIALIZER(usched_list); 50 51 cpumask_t usched_mastermask = CPUMASK_INITIALIZER_ALLONES; 52 53 /* 54 * Called from very low level boot code, i386/i386/machdep.c/init386(). 55 * We cannot do anything fancy. no malloc's, no nothing other then 56 * static initialization. 57 */ 58 struct usched * 59 usched_init(void) 60 { 61 const char *defsched; 62 63 defsched = kgetenv("kern.user_scheduler"); 64 65 /* 66 * Add various userland schedulers to the system. 67 */ 68 usched_ctl(&usched_bsd4, USCH_ADD); 69 usched_ctl(&usched_dfly, USCH_ADD); 70 usched_ctl(&usched_dummy, USCH_ADD); 71 if (defsched == NULL ) 72 return(&usched_dfly); 73 if (strcmp(defsched, "bsd4") == 0) 74 return(&usched_bsd4); 75 if (strcmp(defsched, "dfly") == 0) 76 return(&usched_dfly); 77 kprintf("WARNING: Running dummy userland scheduler\n"); 78 return(&usched_dummy); 79 } 80 81 /* 82 * USCHED_CTL 83 * 84 * SYNOPSIS: 85 * Add/remove usched to/from list. 86 * 87 * ARGUMENTS: 88 * usched - pointer to target scheduler 89 * action - addition or removal ? 90 * 91 * RETURN VALUES: 92 * 0 - success 93 * EINVAL - error 94 */ 95 int 96 usched_ctl(struct usched *usched, int action) 97 { 98 struct usched *item; /* temporaly for TAILQ processing */ 99 int error = 0; 100 101 switch(action) { 102 case USCH_ADD: 103 /* 104 * Make sure it isn't already on the list 105 */ 106 #ifdef INVARIANTS 107 TAILQ_FOREACH(item, &usched_list, entry) { 108 KKASSERT(item != usched); 109 } 110 #endif 111 /* 112 * Optional callback to the scheduler before we officially 113 * add it to the list. 114 */ 115 if (usched->usched_register) 116 usched->usched_register(); 117 TAILQ_INSERT_TAIL(&usched_list, usched, entry); 118 break; 119 case USCH_REM: 120 /* 121 * Do not allow the default scheduler to be removed 122 */ 123 if (strcmp(usched->name, "bsd4") == 0) { 124 error = EINVAL; 125 break; 126 } 127 TAILQ_FOREACH(item, &usched_list, entry) { 128 if (item == usched) 129 break; 130 } 131 if (item) { 132 if (item->usched_unregister) 133 item->usched_unregister(); 134 TAILQ_REMOVE(&usched_list, item, entry); 135 } else { 136 error = EINVAL; 137 } 138 break; 139 default: 140 error = EINVAL; 141 break; 142 } 143 return (error); 144 } 145 146 /* 147 * Called from the scheduler clock on each cpu independently at the 148 * common scheduling rate. If th scheduler clock interrupted a running 149 * lwp the lp will be non-NULL. 150 */ 151 void 152 usched_schedulerclock(struct lwp *lp, sysclock_t periodic, sysclock_t time) 153 { 154 struct usched *item; 155 156 TAILQ_FOREACH(item, &usched_list, entry) { 157 if (lp && lp->lwp_proc->p_usched == item) 158 item->schedulerclock(lp, periodic, time); 159 else 160 item->schedulerclock(NULL, periodic, time); 161 } 162 } 163 164 /* 165 * USCHED_SET(syscall) 166 * 167 * SYNOPSIS: 168 * Setting up a proc's usched. 169 * 170 * ARGUMENTS: 171 * pid - 172 * cmd - 173 * data - 174 * bytes - 175 * RETURN VALUES: 176 * 0 - success 177 * EFBIG - error (invalid cpu#) 178 * EPERM - error (failed to delete cpu#) 179 * EINVAL - error (other reasons) 180 * 181 * MPALMOSTSAFE 182 */ 183 int 184 sys_usched_set(struct usched_set_args *uap) 185 { 186 struct proc *p = curthread->td_proc; 187 struct usched *item; /* temporaly for TAILQ processing */ 188 int error; 189 char buffer[NAME_LENGTH]; 190 cpumask_t mask; 191 struct lwp *lp; 192 int cpuid; 193 194 if (uap->pid != 0 && uap->pid != curthread->td_proc->p_pid) 195 return (EINVAL); 196 197 lp = curthread->td_lwp; 198 get_mplock(); 199 200 switch (uap->cmd) { 201 case USCHED_SET_SCHEDULER: 202 if ((error = priv_check(curthread, PRIV_SCHED_SET)) != 0) 203 break; 204 error = copyinstr(uap->data, buffer, sizeof(buffer), NULL); 205 if (error) 206 break; 207 TAILQ_FOREACH(item, &usched_list, entry) { 208 if ((strcmp(item->name, buffer) == 0)) 209 break; 210 } 211 212 /* 213 * If the scheduler for a process is being changed, disassociate 214 * the old scheduler before switching to the new one. 215 * 216 * XXX we might have to add an additional ABI call to do a 'full 217 * disassociation' and another ABI call to do a 'full 218 * reassociation' 219 */ 220 /* XXX lwp have to deal with multiple lwps here */ 221 if (p->p_nthreads != 1) { 222 error = EINVAL; 223 break; 224 } 225 if (item && item != p->p_usched) { 226 /* XXX lwp */ 227 p->p_usched->release_curproc(ONLY_LWP_IN_PROC(p)); 228 p->p_usched->heuristic_exiting(ONLY_LWP_IN_PROC(p), p); 229 p->p_usched = item; 230 } else if (item == NULL) { 231 error = EINVAL; 232 } 233 break; 234 case USCHED_SET_CPU: 235 if ((error = priv_check(curthread, PRIV_SCHED_CPUSET)) != 0) 236 break; 237 if (uap->bytes != sizeof(int)) { 238 error = EINVAL; 239 break; 240 } 241 error = copyin(uap->data, &cpuid, sizeof(int)); 242 if (error) 243 break; 244 if (cpuid < 0 || cpuid >= ncpus) { 245 error = EFBIG; 246 break; 247 } 248 if (CPUMASK_TESTBIT(smp_active_mask, cpuid) == 0) { 249 error = EINVAL; 250 break; 251 } 252 CPUMASK_ASSBIT(lp->lwp_cpumask, cpuid); 253 if (cpuid != mycpu->gd_cpuid) { 254 lwkt_migratecpu(cpuid); 255 p->p_usched->changedcpu(lp); 256 } 257 break; 258 case USCHED_GET_CPU: 259 /* USCHED_GET_CPU doesn't require special privileges. */ 260 if (uap->bytes != sizeof(int)) { 261 error = EINVAL; 262 break; 263 } 264 error = copyout(&(mycpu->gd_cpuid), uap->data, sizeof(int)); 265 break; 266 case USCHED_GET_CPUMASK: 267 /* USCHED_GET_CPUMASK doesn't require special privileges. */ 268 if (uap->bytes != sizeof(cpumask_t)) { 269 error = EINVAL; 270 break; 271 } 272 error = copyout(&lp->lwp_cpumask, uap->data, sizeof(cpumask_t)); 273 break; 274 case USCHED_ADD_CPU: 275 if ((error = priv_check(curthread, PRIV_SCHED_CPUSET)) != 0) 276 break; 277 if (uap->bytes != sizeof(int)) { 278 error = EINVAL; 279 break; 280 } 281 error = copyin(uap->data, &cpuid, sizeof(int)); 282 if (error) 283 break; 284 if (cpuid < 0 || cpuid >= ncpus) { 285 error = EFBIG; 286 break; 287 } 288 if (CPUMASK_TESTBIT(smp_active_mask, cpuid) == 0) { 289 error = EINVAL; 290 break; 291 } 292 CPUMASK_ORBIT(lp->lwp_cpumask, cpuid); 293 break; 294 case USCHED_DEL_CPU: 295 /* USCHED_DEL_CPU doesn't require special privileges. */ 296 if (uap->bytes != sizeof(int)) { 297 error = EINVAL; 298 break; 299 } 300 error = copyin(uap->data, &cpuid, sizeof(int)); 301 if (error) 302 break; 303 if (cpuid < 0 || cpuid >= ncpus) { 304 error = EFBIG; 305 break; 306 } 307 lp = curthread->td_lwp; 308 mask = lp->lwp_cpumask; 309 CPUMASK_ANDMASK(mask, smp_active_mask); 310 CPUMASK_NANDBIT(mask, cpuid); 311 if (CPUMASK_TESTZERO(mask)) { 312 error = EPERM; 313 } else { 314 CPUMASK_NANDBIT(lp->lwp_cpumask, cpuid); 315 if (CPUMASK_TESTMASK(lp->lwp_cpumask, 316 mycpu->gd_cpumask) == 0) { 317 mask = lp->lwp_cpumask; 318 CPUMASK_ANDMASK(mask, smp_active_mask); 319 cpuid = BSFCPUMASK(mask); 320 lwkt_migratecpu(cpuid); 321 p->p_usched->changedcpu(lp); 322 } 323 } 324 break; 325 default: 326 error = EINVAL; 327 break; 328 } 329 rel_mplock(); 330 return (error); 331 } 332 333