1 /* $NetBSD: linux_sched.c,v 1.29 2005/11/29 22:31:59 jdolecek Exp $ */ 2 3 /*- 4 * Copyright (c) 1999 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 9 * NASA Ames Research Center; by Matthias Scheler. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the NetBSD 22 * Foundation, Inc. and its contributors. 23 * 4. Neither the name of The NetBSD Foundation nor the names of its 24 * contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40 /* 41 * Linux compatibility module. Try to deal with scheduler related syscalls. 42 */ 43 44 #include <sys/cdefs.h> 45 __KERNEL_RCSID(0, "$NetBSD: linux_sched.c,v 1.29 2005/11/29 22:31:59 jdolecek Exp $"); 46 47 #include <sys/param.h> 48 #include <sys/mount.h> 49 #include <sys/proc.h> 50 #include <sys/systm.h> 51 #include <sys/sysctl.h> 52 #include <sys/malloc.h> 53 #include <sys/sa.h> 54 #include <sys/syscallargs.h> 55 #include <sys/wait.h> 56 57 #include <machine/cpu.h> 58 59 #include <compat/linux/common/linux_types.h> 60 #include <compat/linux/common/linux_signal.h> 61 #include <compat/linux/common/linux_machdep.h> /* For LINUX_NPTL */ 62 #include <compat/linux/common/linux_emuldata.h> 63 64 #include <compat/linux/linux_syscallargs.h> 65 66 #include <compat/linux/common/linux_sched.h> 67 68 int 69 linux_sys_clone(l, v, retval) 70 struct lwp *l; 71 void *v; 72 register_t *retval; 73 { 74 struct linux_sys_clone_args /* { 75 syscallarg(int) flags; 76 syscallarg(void *) stack; 77 #ifdef LINUX_NPTL 78 syscallarg(void *) parent_tidptr; 79 syscallarg(void *) child_tidptr; 80 #endif 81 } */ *uap = v; 82 int flags, sig; 83 int error; 84 #ifdef LINUX_NPTL 85 struct linux_emuldata *led; 86 #endif 87 88 /* 89 * We don't support the Linux CLONE_PID or CLONE_PTRACE flags. 90 */ 91 if (SCARG(uap, flags) & (LINUX_CLONE_PID|LINUX_CLONE_PTRACE)) 92 return (EINVAL); 93 94 /* 95 * Thread group implies shared signals. Shared signals 96 * imply shared VM. This matches what Linux kernel does. 97 */ 98 if (SCARG(uap, flags) & LINUX_CLONE_THREAD 99 && (SCARG(uap, flags) & LINUX_CLONE_SIGHAND) == 0) 100 return (EINVAL); 101 if (SCARG(uap, flags) & LINUX_CLONE_SIGHAND 102 && (SCARG(uap, flags) & LINUX_CLONE_VM) == 0) 103 return (EINVAL); 104 105 flags = 0; 106 107 if (SCARG(uap, flags) & LINUX_CLONE_VM) 108 flags |= FORK_SHAREVM; 109 if (SCARG(uap, flags) & LINUX_CLONE_FS) 110 flags |= FORK_SHARECWD; 111 if (SCARG(uap, flags) & LINUX_CLONE_FILES) 112 flags |= FORK_SHAREFILES; 113 if (SCARG(uap, flags) & LINUX_CLONE_SIGHAND) 114 flags |= FORK_SHARESIGS; 115 if (SCARG(uap, flags) & LINUX_CLONE_VFORK) 116 flags |= FORK_PPWAIT; 117 118 /* Thread should not issue a SIGCHLD on termination */ 119 if (SCARG(uap, flags) & LINUX_CLONE_THREAD) { 120 sig = 0; 121 } else { 122 sig = SCARG(uap, flags) & LINUX_CLONE_CSIGNAL; 123 if (sig < 0 || sig >= LINUX__NSIG) 124 return (EINVAL); 125 sig = linux_to_native_signo[sig]; 126 } 127 128 #ifdef LINUX_NPTL 129 led = (struct linux_emuldata *)l->l_proc->p_emuldata; 130 131 if (SCARG(uap, flags) & LINUX_CLONE_PARENT_SETTID) { 132 if (SCARG(uap, parent_tidptr) == NULL) { 133 printf("linux_sys_clone: NULL parent_tidptr\n"); 134 return EINVAL; 135 } 136 137 if ((error = copyout(&l->l_proc->p_pid, 138 SCARG(uap, parent_tidptr), 139 sizeof(l->l_proc->p_pid))) != 0) 140 return error; 141 } 142 143 /* CLONE_CHILD_CLEARTID: TID clear in the child on exit() */ 144 if (SCARG(uap, flags) & LINUX_CLONE_CHILD_CLEARTID) 145 led->child_clear_tid = SCARG(uap, child_tidptr); 146 else 147 led->child_clear_tid = NULL; 148 149 /* CLONE_CHILD_SETTID: TID set in the child on clone() */ 150 if (SCARG(uap, flags) & LINUX_CLONE_CHILD_SETTID) 151 led->child_set_tid = SCARG(uap, child_tidptr); 152 else 153 led->child_set_tid = NULL; 154 155 /* CLONE_SETTLS: new Thread Local Storage in the child */ 156 if (SCARG(uap, flags) & LINUX_CLONE_SETTLS) 157 led->set_tls = linux_get_newtls(l); 158 else 159 led->set_tls = 0; 160 #endif /* LINUX_NPTL */ 161 /* 162 * Note that Linux does not provide a portable way of specifying 163 * the stack area; the caller must know if the stack grows up 164 * or down. So, we pass a stack size of 0, so that the code 165 * that makes this adjustment is a noop. 166 */ 167 if ((error = fork1(l, flags, sig, SCARG(uap, stack), 0, 168 NULL, NULL, retval, NULL)) != 0) 169 return error; 170 171 return 0; 172 } 173 174 int 175 linux_sys_sched_setparam(cl, v, retval) 176 struct lwp *cl; 177 void *v; 178 register_t *retval; 179 { 180 struct linux_sys_sched_setparam_args /* { 181 syscallarg(linux_pid_t) pid; 182 syscallarg(const struct linux_sched_param *) sp; 183 } */ *uap = v; 184 struct proc *cp = cl->l_proc; 185 int error; 186 struct linux_sched_param lp; 187 struct proc *p; 188 189 /* 190 * We only check for valid parameters and return afterwards. 191 */ 192 193 if (SCARG(uap, pid) < 0 || SCARG(uap, sp) == NULL) 194 return EINVAL; 195 196 error = copyin(SCARG(uap, sp), &lp, sizeof(lp)); 197 if (error) 198 return error; 199 200 if (SCARG(uap, pid) != 0) { 201 struct pcred *pc = cp->p_cred; 202 203 if ((p = pfind(SCARG(uap, pid))) == NULL) 204 return ESRCH; 205 if (!(cp == p || 206 pc->pc_ucred->cr_uid == 0 || 207 pc->p_ruid == p->p_cred->p_ruid || 208 pc->pc_ucred->cr_uid == p->p_cred->p_ruid || 209 pc->p_ruid == p->p_ucred->cr_uid || 210 pc->pc_ucred->cr_uid == p->p_ucred->cr_uid)) 211 return EPERM; 212 } 213 214 return 0; 215 } 216 217 int 218 linux_sys_sched_getparam(cl, v, retval) 219 struct lwp *cl; 220 void *v; 221 register_t *retval; 222 { 223 struct linux_sys_sched_getparam_args /* { 224 syscallarg(linux_pid_t) pid; 225 syscallarg(struct linux_sched_param *) sp; 226 } */ *uap = v; 227 struct proc *cp = cl->l_proc; 228 struct proc *p; 229 struct linux_sched_param lp; 230 231 /* 232 * We only check for valid parameters and return a dummy priority afterwards. 233 */ 234 if (SCARG(uap, pid) < 0 || SCARG(uap, sp) == NULL) 235 return EINVAL; 236 237 if (SCARG(uap, pid) != 0) { 238 struct pcred *pc = cp->p_cred; 239 240 if ((p = pfind(SCARG(uap, pid))) == NULL) 241 return ESRCH; 242 if (!(cp == p || 243 pc->pc_ucred->cr_uid == 0 || 244 pc->p_ruid == p->p_cred->p_ruid || 245 pc->pc_ucred->cr_uid == p->p_cred->p_ruid || 246 pc->p_ruid == p->p_ucred->cr_uid || 247 pc->pc_ucred->cr_uid == p->p_ucred->cr_uid)) 248 return EPERM; 249 } 250 251 lp.sched_priority = 0; 252 return copyout(&lp, SCARG(uap, sp), sizeof(lp)); 253 } 254 255 int 256 linux_sys_sched_setscheduler(cl, v, retval) 257 struct lwp *cl; 258 void *v; 259 register_t *retval; 260 { 261 struct linux_sys_sched_setscheduler_args /* { 262 syscallarg(linux_pid_t) pid; 263 syscallarg(int) policy; 264 syscallarg(cont struct linux_sched_scheduler *) sp; 265 } */ *uap = v; 266 struct proc *cp = cl->l_proc; 267 int error; 268 struct linux_sched_param lp; 269 struct proc *p; 270 271 /* 272 * We only check for valid parameters and return afterwards. 273 */ 274 275 if (SCARG(uap, pid) < 0 || SCARG(uap, sp) == NULL) 276 return EINVAL; 277 278 error = copyin(SCARG(uap, sp), &lp, sizeof(lp)); 279 if (error) 280 return error; 281 282 if (SCARG(uap, pid) != 0) { 283 struct pcred *pc = cp->p_cred; 284 285 if ((p = pfind(SCARG(uap, pid))) == NULL) 286 return ESRCH; 287 if (!(cp == p || 288 pc->pc_ucred->cr_uid == 0 || 289 pc->p_ruid == p->p_cred->p_ruid || 290 pc->pc_ucred->cr_uid == p->p_cred->p_ruid || 291 pc->p_ruid == p->p_ucred->cr_uid || 292 pc->pc_ucred->cr_uid == p->p_ucred->cr_uid)) 293 return EPERM; 294 } 295 296 /* 297 * We can't emulate anything put the default scheduling policy. 298 */ 299 if (SCARG(uap, policy) != LINUX_SCHED_OTHER || lp.sched_priority != 0) 300 return EINVAL; 301 302 return 0; 303 } 304 305 int 306 linux_sys_sched_getscheduler(cl, v, retval) 307 struct lwp *cl; 308 void *v; 309 register_t *retval; 310 { 311 struct linux_sys_sched_getscheduler_args /* { 312 syscallarg(linux_pid_t) pid; 313 } */ *uap = v; 314 struct proc *cp = cl->l_proc; 315 struct proc *p; 316 317 *retval = -1; 318 /* 319 * We only check for valid parameters and return afterwards. 320 */ 321 322 if (SCARG(uap, pid) != 0) { 323 struct pcred *pc = cp->p_cred; 324 325 if ((p = pfind(SCARG(uap, pid))) == NULL) 326 return ESRCH; 327 if (!(cp == p || 328 pc->pc_ucred->cr_uid == 0 || 329 pc->p_ruid == p->p_cred->p_ruid || 330 pc->pc_ucred->cr_uid == p->p_cred->p_ruid || 331 pc->p_ruid == p->p_ucred->cr_uid || 332 pc->pc_ucred->cr_uid == p->p_ucred->cr_uid)) 333 return EPERM; 334 } 335 336 /* 337 * We can't emulate anything put the default scheduling policy. 338 */ 339 *retval = LINUX_SCHED_OTHER; 340 return 0; 341 } 342 343 int 344 linux_sys_sched_yield(cl, v, retval) 345 struct lwp *cl; 346 void *v; 347 register_t *retval; 348 { 349 350 yield(); 351 return 0; 352 } 353 354 int 355 linux_sys_sched_get_priority_max(cl, v, retval) 356 struct lwp *cl; 357 void *v; 358 register_t *retval; 359 { 360 struct linux_sys_sched_get_priority_max_args /* { 361 syscallarg(int) policy; 362 } */ *uap = v; 363 364 /* 365 * We can't emulate anything put the default scheduling policy. 366 */ 367 if (SCARG(uap, policy) != LINUX_SCHED_OTHER) { 368 *retval = -1; 369 return EINVAL; 370 } 371 372 *retval = 0; 373 return 0; 374 } 375 376 int 377 linux_sys_sched_get_priority_min(cl, v, retval) 378 struct lwp *cl; 379 void *v; 380 register_t *retval; 381 { 382 struct linux_sys_sched_get_priority_min_args /* { 383 syscallarg(int) policy; 384 } */ *uap = v; 385 386 /* 387 * We can't emulate anything put the default scheduling policy. 388 */ 389 if (SCARG(uap, policy) != LINUX_SCHED_OTHER) { 390 *retval = -1; 391 return EINVAL; 392 } 393 394 *retval = 0; 395 return 0; 396 } 397 398 #ifndef __m68k__ 399 /* Present on everything but m68k */ 400 int 401 linux_sys_exit_group(l, v, retval) 402 struct lwp *l; 403 void *v; 404 register_t *retval; 405 { 406 struct linux_sys_exit_group_args /* { 407 syscallarg(int) error_code; 408 } */ *uap = v; 409 410 /* 411 * XXX The calling thread is supposed to kill all threads 412 * in the same thread group (i.e. all threads created 413 * via clone(2) with CLONE_THREAD flag set). This appears 414 * to not be used yet, so the thread group handling 415 * is currently not implemented. 416 */ 417 418 exit1(l, W_EXITCODE(SCARG(uap, error_code), 0)); 419 /* NOTREACHED */ 420 return 0; 421 } 422 #endif /* !__m68k__ */ 423 424 #ifdef LINUX_NPTL 425 int 426 linux_sys_set_tid_address(l, v, retval) 427 struct lwp *l; 428 void *v; 429 register_t *retval; 430 { 431 struct linux_sys_set_tid_address_args /* { 432 syscallarg(int *) tidptr; 433 } */ *uap = v; 434 struct linux_emuldata *led; 435 436 led = (struct linux_emuldata *)l->l_proc->p_emuldata; 437 led->clear_tid = SCARG(uap, tid); 438 439 *retval = l->l_proc->p_pid; 440 441 return 0; 442 } 443 444 /* ARGUSED1 */ 445 int 446 linux_sys_gettid(l, v, retval) 447 struct lwp *l; 448 void *v; 449 register_t *retval; 450 { 451 *retval = l->l_proc->p_pid; 452 return 0; 453 } 454 455 int 456 linux_sys_sched_getaffinity(l, v, retval) 457 struct lwp *l; 458 void *v; 459 register_t *retval; 460 { 461 struct linux_sys_sched_getaffinity_args /* { 462 syscallarg(pid_t) pid; 463 syscallarg(unsigned int) len; 464 syscallarg(unsigned long *) mask; 465 } */ *uap = v; 466 int error; 467 int ret; 468 int ncpu; 469 int name[2]; 470 size_t sz; 471 char *data; 472 int *retp; 473 474 if (SCARG(uap, mask) == NULL) 475 return EINVAL; 476 477 if (SCARG(uap, len) < sizeof(int)) 478 return EINVAL; 479 480 if (pfind(SCARG(uap, pid)) == NULL) 481 return ESRCH; 482 483 /* 484 * return the actual number of CPU, tag all of them as available 485 * The result is a mask, the first CPU being in the least significant 486 * bit. 487 */ 488 name[0] = CTL_HW; 489 name[1] = HW_NCPU; 490 sz = sizeof(ncpu); 491 492 if ((error = old_sysctl(&name[0], 2, &ncpu, &sz, NULL, 0, NULL)) != 0) 493 return error; 494 495 ret = (1 << ncpu) - 1; 496 497 data = malloc(SCARG(uap, len), M_TEMP, M_WAITOK|M_ZERO); 498 retp = (int *)&data[SCARG(uap, len) - sizeof(ret)]; 499 *retp = ret; 500 501 if ((error = copyout(data, SCARG(uap, mask), SCARG(uap, len))) != 0) 502 return error; 503 504 free(data, M_TEMP); 505 506 return 0; 507 508 } 509 510 int 511 linux_sys_sched_setaffinity(l, v, retval) 512 struct lwp *l; 513 void *v; 514 register_t *retval; 515 { 516 struct linux_sys_sched_setaffinity_args /* { 517 syscallarg(pid_t) pid; 518 syscallarg(unsigned int) len; 519 syscallarg(unsigned long *) mask; 520 } */ *uap = v; 521 522 if (pfind(SCARG(uap, pid)) == NULL) 523 return ESRCH; 524 525 /* Let's ignore it */ 526 #ifdef DEBUG_LINUX 527 printf("linux_sys_sched_setaffinity\n"); 528 #endif 529 return 0; 530 }; 531 #endif /* LINUX_NPTL */ 532