1 /* $NetBSD: linux_sched.c,v 1.45 2007/12/08 18:36:09 dsl 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.45 2007/12/08 18:36:09 dsl 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/syscallargs.h> 54 #include <sys/wait.h> 55 #include <sys/kauth.h> 56 #include <sys/ptrace.h> 57 58 #include <sys/cpu.h> 59 60 #include <compat/linux/common/linux_types.h> 61 #include <compat/linux/common/linux_signal.h> 62 #include <compat/linux/common/linux_machdep.h> /* For LINUX_NPTL */ 63 #include <compat/linux/common/linux_emuldata.h> 64 #include <compat/linux/common/linux_ipc.h> 65 #include <compat/linux/common/linux_sem.h> 66 67 #include <compat/linux/linux_syscallargs.h> 68 69 #include <compat/linux/common/linux_sched.h> 70 71 int 72 linux_sys_clone(struct lwp *l, void *v, 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 sig = SCARG(uap, flags) & LINUX_CLONE_CSIGNAL; 119 if (sig < 0 || sig >= LINUX__NSIG) 120 return (EINVAL); 121 sig = linux_to_native_signo[sig]; 122 123 #ifdef LINUX_NPTL 124 led = (struct linux_emuldata *)l->l_proc->p_emuldata; 125 126 led->parent_tidptr = SCARG(uap, parent_tidptr); 127 led->child_tidptr = SCARG(uap, child_tidptr); 128 led->clone_flags = SCARG(uap, flags); 129 #endif /* LINUX_NPTL */ 130 131 /* 132 * Note that Linux does not provide a portable way of specifying 133 * the stack area; the caller must know if the stack grows up 134 * or down. So, we pass a stack size of 0, so that the code 135 * that makes this adjustment is a noop. 136 */ 137 if ((error = fork1(l, flags, sig, SCARG(uap, stack), 0, 138 NULL, NULL, retval, NULL)) != 0) 139 return error; 140 141 return 0; 142 } 143 144 int 145 linux_sys_sched_setparam(struct lwp *cl, void *v, register_t *retval) 146 { 147 struct linux_sys_sched_setparam_args /* { 148 syscallarg(linux_pid_t) pid; 149 syscallarg(const struct linux_sched_param *) sp; 150 } */ *uap = v; 151 int error; 152 struct linux_sched_param lp; 153 struct proc *p; 154 155 /* 156 * We only check for valid parameters and return afterwards. 157 */ 158 159 if (SCARG(uap, pid) < 0 || SCARG(uap, sp) == NULL) 160 return EINVAL; 161 162 error = copyin(SCARG(uap, sp), &lp, sizeof(lp)); 163 if (error) 164 return error; 165 166 if (SCARG(uap, pid) != 0) { 167 kauth_cred_t pc = cl->l_cred; 168 169 if ((p = pfind(SCARG(uap, pid))) == NULL) 170 return ESRCH; 171 if (!(cl->l_proc == p || 172 kauth_authorize_generic(pc, KAUTH_GENERIC_ISSUSER, NULL) == 0 || 173 kauth_cred_getuid(pc) == kauth_cred_getuid(p->p_cred) || 174 kauth_cred_geteuid(pc) == kauth_cred_getuid(p->p_cred) || 175 kauth_cred_getuid(pc) == kauth_cred_geteuid(p->p_cred) || 176 kauth_cred_geteuid(pc) == kauth_cred_geteuid(p->p_cred))) 177 return EPERM; 178 } 179 180 return 0; 181 } 182 183 int 184 linux_sys_sched_getparam(struct lwp *cl, void *v, register_t *retval) 185 { 186 struct linux_sys_sched_getparam_args /* { 187 syscallarg(linux_pid_t) pid; 188 syscallarg(struct linux_sched_param *) sp; 189 } */ *uap = v; 190 struct proc *p; 191 struct linux_sched_param lp; 192 193 /* 194 * We only check for valid parameters and return a dummy priority afterwards. 195 */ 196 if (SCARG(uap, pid) < 0 || SCARG(uap, sp) == NULL) 197 return EINVAL; 198 199 if (SCARG(uap, pid) != 0) { 200 kauth_cred_t pc = cl->l_cred; 201 202 if ((p = pfind(SCARG(uap, pid))) == NULL) 203 return ESRCH; 204 if (!(cl->l_proc == p || 205 kauth_authorize_generic(pc, KAUTH_GENERIC_ISSUSER, NULL) == 0 || 206 kauth_cred_getuid(pc) == kauth_cred_getuid(p->p_cred) || 207 kauth_cred_geteuid(pc) == kauth_cred_getuid(p->p_cred) || 208 kauth_cred_getuid(pc) == kauth_cred_geteuid(p->p_cred) || 209 kauth_cred_geteuid(pc) == kauth_cred_geteuid(p->p_cred))) 210 return EPERM; 211 } 212 213 lp.sched_priority = 0; 214 return copyout(&lp, SCARG(uap, sp), sizeof(lp)); 215 } 216 217 int 218 linux_sys_sched_setscheduler(struct lwp *cl, void *v, 219 register_t *retval) 220 { 221 struct linux_sys_sched_setscheduler_args /* { 222 syscallarg(linux_pid_t) pid; 223 syscallarg(int) policy; 224 syscallarg(cont struct linux_sched_scheduler *) sp; 225 } */ *uap = v; 226 int error; 227 struct linux_sched_param lp; 228 struct proc *p; 229 230 /* 231 * We only check for valid parameters and return afterwards. 232 */ 233 234 if (SCARG(uap, pid) < 0 || SCARG(uap, sp) == NULL) 235 return EINVAL; 236 237 error = copyin(SCARG(uap, sp), &lp, sizeof(lp)); 238 if (error) 239 return error; 240 241 if (SCARG(uap, pid) != 0) { 242 kauth_cred_t pc = cl->l_cred; 243 244 if ((p = pfind(SCARG(uap, pid))) == NULL) 245 return ESRCH; 246 if (!(cl->l_proc == p || 247 kauth_authorize_generic(pc, KAUTH_GENERIC_ISSUSER, NULL) == 0 || 248 kauth_cred_getuid(pc) == kauth_cred_getuid(p->p_cred) || 249 kauth_cred_geteuid(pc) == kauth_cred_getuid(p->p_cred) || 250 kauth_cred_getuid(pc) == kauth_cred_geteuid(p->p_cred) || 251 kauth_cred_geteuid(pc) == kauth_cred_geteuid(p->p_cred))) 252 return EPERM; 253 } 254 255 return 0; 256 /* 257 * We can't emulate anything put the default scheduling policy. 258 */ 259 if (SCARG(uap, policy) != LINUX_SCHED_OTHER || lp.sched_priority != 0) 260 return EINVAL; 261 262 return 0; 263 } 264 265 int 266 linux_sys_sched_getscheduler(struct lwp *cl, void *v, register_t *retval) 267 { 268 struct linux_sys_sched_getscheduler_args /* { 269 syscallarg(linux_pid_t) pid; 270 } */ *uap = v; 271 struct proc *p; 272 273 *retval = -1; 274 /* 275 * We only check for valid parameters and return afterwards. 276 */ 277 278 if (SCARG(uap, pid) != 0) { 279 kauth_cred_t pc = cl->l_cred; 280 281 if ((p = pfind(SCARG(uap, pid))) == NULL) 282 return ESRCH; 283 if (!(cl->l_proc == p || 284 kauth_authorize_generic(pc, KAUTH_GENERIC_ISSUSER, NULL) == 0 || 285 kauth_cred_getuid(pc) == kauth_cred_getuid(p->p_cred) || 286 kauth_cred_geteuid(pc) == kauth_cred_getuid(p->p_cred) || 287 kauth_cred_getuid(pc) == kauth_cred_geteuid(p->p_cred) || 288 kauth_cred_geteuid(pc) == kauth_cred_geteuid(p->p_cred))) 289 return EPERM; 290 } 291 292 /* 293 * We can't emulate anything put the default scheduling policy. 294 */ 295 *retval = LINUX_SCHED_OTHER; 296 return 0; 297 } 298 299 int 300 linux_sys_sched_yield(struct lwp *cl, void *v, 301 register_t *retval) 302 { 303 304 yield(); 305 return 0; 306 } 307 308 int 309 linux_sys_sched_get_priority_max(struct lwp *cl, void *v, 310 register_t *retval) 311 { 312 struct linux_sys_sched_get_priority_max_args /* { 313 syscallarg(int) policy; 314 } */ *uap = v; 315 316 /* 317 * We can't emulate anything put the default scheduling policy. 318 */ 319 if (SCARG(uap, policy) != LINUX_SCHED_OTHER) { 320 *retval = -1; 321 return EINVAL; 322 } 323 324 *retval = 0; 325 return 0; 326 } 327 328 int 329 linux_sys_sched_get_priority_min(struct lwp *cl, void *v, 330 register_t *retval) 331 { 332 struct linux_sys_sched_get_priority_min_args /* { 333 syscallarg(int) policy; 334 } */ *uap = v; 335 336 /* 337 * We can't emulate anything put the default scheduling policy. 338 */ 339 if (SCARG(uap, policy) != LINUX_SCHED_OTHER) { 340 *retval = -1; 341 return EINVAL; 342 } 343 344 *retval = 0; 345 return 0; 346 } 347 348 #ifndef __m68k__ 349 /* Present on everything but m68k */ 350 int 351 linux_sys_exit_group(struct lwp *l, void *v, register_t *retval) 352 { 353 #ifdef LINUX_NPTL 354 struct linux_sys_exit_group_args /* { 355 syscallarg(int) error_code; 356 } */ *uap = v; 357 struct proc *p = l->l_proc; 358 struct linux_emuldata *led = p->p_emuldata; 359 struct linux_emuldata *e; 360 361 if (led->s->flags & LINUX_LES_USE_NPTL) { 362 363 #ifdef DEBUG_LINUX 364 printf("%s:%d, led->s->refs = %d\n", __func__, __LINE__, 365 led->s->refs); 366 #endif 367 368 /* 369 * The calling thread is supposed to kill all threads 370 * in the same thread group (i.e. all threads created 371 * via clone(2) with CLONE_THREAD flag set). 372 * 373 * If there is only one thread, things are quite simple 374 */ 375 if (led->s->refs == 1) 376 return sys_exit(l, v, retval); 377 378 #ifdef DEBUG_LINUX 379 printf("%s:%d\n", __func__, __LINE__); 380 #endif 381 382 led->s->flags |= LINUX_LES_INEXITGROUP; 383 led->s->xstat = W_EXITCODE(SCARG(uap, error_code), 0); 384 385 /* 386 * Kill all threads in the group. The emulation exit hook takes 387 * care of hiding the zombies and reporting the exit code 388 * properly. 389 */ 390 mutex_enter(&proclist_mutex); 391 LIST_FOREACH(e, &led->s->threads, threads) { 392 if (e->proc == p) 393 continue; 394 395 #ifdef DEBUG_LINUX 396 printf("%s: kill PID %d\n", __func__, e->proc->p_pid); 397 #endif 398 psignal(e->proc, SIGKILL); 399 } 400 401 /* Now, kill ourselves */ 402 psignal(p, SIGKILL); 403 mutex_exit(&proclist_mutex); 404 405 return 0; 406 407 } 408 #endif /* LINUX_NPTL */ 409 410 return sys_exit(l, v, retval); 411 } 412 #endif /* !__m68k__ */ 413 414 #ifdef LINUX_NPTL 415 int 416 linux_sys_set_tid_address(struct lwp *l, void *v, register_t *retval) 417 { 418 struct linux_sys_set_tid_address_args /* { 419 syscallarg(int *) tidptr; 420 } */ *uap = v; 421 struct linux_emuldata *led; 422 423 led = (struct linux_emuldata *)l->l_proc->p_emuldata; 424 led->clear_tid = SCARG(uap, tid); 425 426 led->s->flags |= LINUX_LES_USE_NPTL; 427 428 *retval = l->l_proc->p_pid; 429 430 return 0; 431 } 432 433 /* ARGUSED1 */ 434 int 435 linux_sys_gettid(struct lwp *l, void *v, register_t *retval) 436 { 437 /* The Linux kernel does it exactly that way */ 438 *retval = l->l_proc->p_pid; 439 return 0; 440 } 441 442 #ifdef LINUX_NPTL 443 /* ARGUSED1 */ 444 int 445 linux_sys_getpid(struct lwp *l, void *v, register_t *retval) 446 { 447 struct linux_emuldata *led = l->l_proc->p_emuldata; 448 449 if (led->s->flags & LINUX_LES_USE_NPTL) { 450 /* The Linux kernel does it exactly that way */ 451 *retval = led->s->group_pid; 452 } else { 453 *retval = l->l_proc->p_pid; 454 } 455 456 return 0; 457 } 458 459 /* ARGUSED1 */ 460 int 461 linux_sys_getppid(struct lwp *l, void *v, register_t *retval) 462 { 463 struct proc *p = l->l_proc; 464 struct linux_emuldata *led = p->p_emuldata; 465 struct proc *glp; 466 struct proc *pp; 467 468 if (led->s->flags & LINUX_LES_USE_NPTL) { 469 470 /* Find the thread group leader's parent */ 471 if ((glp = pfind(led->s->group_pid)) == NULL) { 472 /* Maybe panic... */ 473 printf("linux_sys_getppid: missing group leader PID" 474 " %d\n", led->s->group_pid); 475 return -1; 476 } 477 pp = glp->p_pptr; 478 479 /* If this is a Linux process too, return thread group PID */ 480 if (pp->p_emul == p->p_emul) { 481 struct linux_emuldata *pled; 482 483 pled = pp->p_emuldata; 484 *retval = pled->s->group_pid; 485 } else { 486 *retval = pp->p_pid; 487 } 488 489 } else { 490 *retval = p->p_pptr->p_pid; 491 } 492 493 return 0; 494 } 495 #endif /* LINUX_NPTL */ 496 497 int 498 linux_sys_sched_getaffinity(struct lwp *l, void *v, register_t *retval) 499 { 500 struct linux_sys_sched_getaffinity_args /* { 501 syscallarg(pid_t) pid; 502 syscallarg(unsigned int) len; 503 syscallarg(unsigned long *) mask; 504 } */ *uap = v; 505 int error; 506 int ret; 507 char *data; 508 int *retp; 509 510 if (SCARG(uap, mask) == NULL) 511 return EINVAL; 512 513 if (SCARG(uap, len) < sizeof(int)) 514 return EINVAL; 515 516 if (pfind(SCARG(uap, pid)) == NULL) 517 return ESRCH; 518 519 /* 520 * return the actual number of CPU, tag all of them as available 521 * The result is a mask, the first CPU being in the least significant 522 * bit. 523 */ 524 ret = (1 << ncpu) - 1; 525 data = malloc(SCARG(uap, len), M_TEMP, M_WAITOK|M_ZERO); 526 retp = (int *)&data[SCARG(uap, len) - sizeof(ret)]; 527 *retp = ret; 528 529 if ((error = copyout(data, SCARG(uap, mask), SCARG(uap, len))) != 0) 530 return error; 531 532 free(data, M_TEMP); 533 534 return 0; 535 536 } 537 538 int 539 linux_sys_sched_setaffinity(struct lwp *l, void *v, register_t *retval) 540 { 541 struct linux_sys_sched_setaffinity_args /* { 542 syscallarg(pid_t) pid; 543 syscallarg(unsigned int) len; 544 syscallarg(unsigned long *) mask; 545 } */ *uap = v; 546 547 if (pfind(SCARG(uap, pid)) == NULL) 548 return ESRCH; 549 550 /* Let's ignore it */ 551 #ifdef DEBUG_LINUX 552 printf("linux_sys_sched_setaffinity\n"); 553 #endif 554 return 0; 555 }; 556 #endif /* LINUX_NPTL */ 557