1 /* $NetBSD: linux_sched.c,v 1.48 2008/02/16 16:39:35 elad 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.48 2008/02/16 16:39:35 elad 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, const struct linux_sys_clone_args *uap, register_t *retval) 73 { 74 /* { 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 } */ 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 *l, const struct linux_sys_sched_setparam_args *uap, register_t *retval) 146 { 147 /* { 148 syscallarg(linux_pid_t) pid; 149 syscallarg(const struct linux_sched_param *) sp; 150 } */ 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 if ((p = pfind(SCARG(uap, pid))) == NULL) 168 return ESRCH; 169 170 if (kauth_authorize_process(l->l_cred, 171 KAUTH_PROCESS_SCHEDULER_SETPARAM, p, NULL, NULL, NULL) != 0) 172 return EPERM; 173 } 174 175 return 0; 176 } 177 178 int 179 linux_sys_sched_getparam(struct lwp *l, const struct linux_sys_sched_getparam_args *uap, register_t *retval) 180 { 181 /* { 182 syscallarg(linux_pid_t) pid; 183 syscallarg(struct linux_sched_param *) sp; 184 } */ 185 struct proc *p; 186 struct linux_sched_param lp; 187 188 /* 189 * We only check for valid parameters and return a dummy priority afterwards. 190 */ 191 if (SCARG(uap, pid) < 0 || SCARG(uap, sp) == NULL) 192 return EINVAL; 193 194 if (SCARG(uap, pid) != 0) { 195 if ((p = pfind(SCARG(uap, pid))) == NULL) 196 return ESRCH; 197 198 if (kauth_authorize_process(l->l_cred, 199 KAUTH_PROCESS_SCHEDULER_GETPARAM, p, NULL, NULL, NULL) != 0) 200 return EPERM; 201 } 202 203 lp.sched_priority = 0; 204 return copyout(&lp, SCARG(uap, sp), sizeof(lp)); 205 } 206 207 int 208 linux_sys_sched_setscheduler(struct lwp *l, const struct linux_sys_sched_setscheduler_args *uap, register_t *retval) 209 { 210 /* { 211 syscallarg(linux_pid_t) pid; 212 syscallarg(int) policy; 213 syscallarg(cont struct linux_sched_scheduler *) sp; 214 } */ 215 int error; 216 struct linux_sched_param lp; 217 struct proc *p; 218 219 /* 220 * We only check for valid parameters and return afterwards. 221 */ 222 223 if (SCARG(uap, pid) < 0 || SCARG(uap, sp) == NULL) 224 return EINVAL; 225 226 error = copyin(SCARG(uap, sp), &lp, sizeof(lp)); 227 if (error) 228 return error; 229 230 if (SCARG(uap, pid) != 0) { 231 if ((p = pfind(SCARG(uap, pid))) == NULL) 232 return ESRCH; 233 234 if (kauth_authorize_process(l->l_cred, 235 KAUTH_PROCESS_SCHEDULER_SET, p, NULL, NULL, NULL) != 0) 236 return EPERM; 237 } 238 239 return 0; 240 /* 241 * We can't emulate anything put the default scheduling policy. 242 */ 243 if (SCARG(uap, policy) != LINUX_SCHED_OTHER || lp.sched_priority != 0) 244 return EINVAL; 245 246 return 0; 247 } 248 249 int 250 linux_sys_sched_getscheduler(struct lwp *l, const struct linux_sys_sched_getscheduler_args *uap, register_t *retval) 251 { 252 /* { 253 syscallarg(linux_pid_t) pid; 254 } */ 255 struct proc *p; 256 257 *retval = -1; 258 /* 259 * We only check for valid parameters and return afterwards. 260 */ 261 262 if (SCARG(uap, pid) != 0) { 263 if ((p = pfind(SCARG(uap, pid))) == NULL) 264 return ESRCH; 265 266 if (kauth_authorize_process(l->l_cred, 267 KAUTH_PROCESS_SCHEDULER_GET, p, NULL, NULL, NULL) != 0) 268 return EPERM; 269 } 270 271 /* 272 * We can't emulate anything put the default scheduling policy. 273 */ 274 *retval = LINUX_SCHED_OTHER; 275 return 0; 276 } 277 278 int 279 linux_sys_sched_yield(struct lwp *l, const void *v, register_t *retval) 280 { 281 282 yield(); 283 return 0; 284 } 285 286 int 287 linux_sys_sched_get_priority_max(struct lwp *l, const struct linux_sys_sched_get_priority_max_args *uap, register_t *retval) 288 { 289 /* { 290 syscallarg(int) policy; 291 } */ 292 293 /* 294 * We can't emulate anything put the default scheduling policy. 295 */ 296 if (SCARG(uap, policy) != LINUX_SCHED_OTHER) { 297 *retval = -1; 298 return EINVAL; 299 } 300 301 *retval = 0; 302 return 0; 303 } 304 305 int 306 linux_sys_sched_get_priority_min(struct lwp *l, const struct linux_sys_sched_get_priority_min_args *uap, register_t *retval) 307 { 308 /* { 309 syscallarg(int) policy; 310 } */ 311 312 /* 313 * We can't emulate anything put the default scheduling policy. 314 */ 315 if (SCARG(uap, policy) != LINUX_SCHED_OTHER) { 316 *retval = -1; 317 return EINVAL; 318 } 319 320 *retval = 0; 321 return 0; 322 } 323 324 #ifndef __m68k__ 325 /* Present on everything but m68k */ 326 int 327 linux_sys_exit_group(struct lwp *l, const struct linux_sys_exit_group_args *uap, register_t *retval) 328 { 329 #ifdef LINUX_NPTL 330 /* { 331 syscallarg(int) error_code; 332 } */ 333 struct proc *p = l->l_proc; 334 struct linux_emuldata *led = p->p_emuldata; 335 struct linux_emuldata *e; 336 337 if (led->s->flags & LINUX_LES_USE_NPTL) { 338 339 #ifdef DEBUG_LINUX 340 printf("%s:%d, led->s->refs = %d\n", __func__, __LINE__, 341 led->s->refs); 342 #endif 343 344 /* 345 * The calling thread is supposed to kill all threads 346 * in the same thread group (i.e. all threads created 347 * via clone(2) with CLONE_THREAD flag set). 348 * 349 * If there is only one thread, things are quite simple 350 */ 351 if (led->s->refs == 1) 352 return sys_exit(l, (const void *)uap, retval); 353 354 #ifdef DEBUG_LINUX 355 printf("%s:%d\n", __func__, __LINE__); 356 #endif 357 358 led->s->flags |= LINUX_LES_INEXITGROUP; 359 led->s->xstat = W_EXITCODE(SCARG(uap, error_code), 0); 360 361 /* 362 * Kill all threads in the group. The emulation exit hook takes 363 * care of hiding the zombies and reporting the exit code 364 * properly. 365 */ 366 mutex_enter(&proclist_mutex); 367 LIST_FOREACH(e, &led->s->threads, threads) { 368 if (e->proc == p) 369 continue; 370 371 #ifdef DEBUG_LINUX 372 printf("%s: kill PID %d\n", __func__, e->proc->p_pid); 373 #endif 374 psignal(e->proc, SIGKILL); 375 } 376 377 /* Now, kill ourselves */ 378 psignal(p, SIGKILL); 379 mutex_exit(&proclist_mutex); 380 381 return 0; 382 383 } 384 #endif /* LINUX_NPTL */ 385 386 return sys_exit(l, (const void *)uap, retval); 387 } 388 #endif /* !__m68k__ */ 389 390 #ifdef LINUX_NPTL 391 int 392 linux_sys_set_tid_address(struct lwp *l, const struct linux_sys_set_tid_address_args *uap, register_t *retval) 393 { 394 /* { 395 syscallarg(int *) tidptr; 396 } */ 397 struct linux_emuldata *led; 398 399 led = (struct linux_emuldata *)l->l_proc->p_emuldata; 400 led->clear_tid = SCARG(uap, tid); 401 402 led->s->flags |= LINUX_LES_USE_NPTL; 403 404 *retval = l->l_proc->p_pid; 405 406 return 0; 407 } 408 409 /* ARGUSED1 */ 410 int 411 linux_sys_gettid(struct lwp *l, const void *v, register_t *retval) 412 { 413 /* The Linux kernel does it exactly that way */ 414 *retval = l->l_proc->p_pid; 415 return 0; 416 } 417 418 #ifdef LINUX_NPTL 419 /* ARGUSED1 */ 420 int 421 linux_sys_getpid(struct lwp *l, const void *v, register_t *retval) 422 { 423 struct linux_emuldata *led = l->l_proc->p_emuldata; 424 425 if (led->s->flags & LINUX_LES_USE_NPTL) { 426 /* The Linux kernel does it exactly that way */ 427 *retval = led->s->group_pid; 428 } else { 429 *retval = l->l_proc->p_pid; 430 } 431 432 return 0; 433 } 434 435 /* ARGUSED1 */ 436 int 437 linux_sys_getppid(struct lwp *l, const void *v, register_t *retval) 438 { 439 struct proc *p = l->l_proc; 440 struct linux_emuldata *led = p->p_emuldata; 441 struct proc *glp; 442 struct proc *pp; 443 444 if (led->s->flags & LINUX_LES_USE_NPTL) { 445 446 /* Find the thread group leader's parent */ 447 if ((glp = pfind(led->s->group_pid)) == NULL) { 448 /* Maybe panic... */ 449 printf("linux_sys_getppid: missing group leader PID" 450 " %d\n", led->s->group_pid); 451 return -1; 452 } 453 pp = glp->p_pptr; 454 455 /* If this is a Linux process too, return thread group PID */ 456 if (pp->p_emul == p->p_emul) { 457 struct linux_emuldata *pled; 458 459 pled = pp->p_emuldata; 460 *retval = pled->s->group_pid; 461 } else { 462 *retval = pp->p_pid; 463 } 464 465 } else { 466 *retval = p->p_pptr->p_pid; 467 } 468 469 return 0; 470 } 471 #endif /* LINUX_NPTL */ 472 473 int 474 linux_sys_sched_getaffinity(struct lwp *l, const struct linux_sys_sched_getaffinity_args *uap, register_t *retval) 475 { 476 /* { 477 syscallarg(pid_t) pid; 478 syscallarg(unsigned int) len; 479 syscallarg(unsigned long *) mask; 480 } */ 481 int error; 482 int ret; 483 char *data; 484 int *retp; 485 486 if (SCARG(uap, mask) == NULL) 487 return EINVAL; 488 489 if (SCARG(uap, len) < sizeof(int)) 490 return EINVAL; 491 492 if (pfind(SCARG(uap, pid)) == NULL) 493 return ESRCH; 494 495 /* 496 * return the actual number of CPU, tag all of them as available 497 * The result is a mask, the first CPU being in the least significant 498 * bit. 499 */ 500 ret = (1 << ncpu) - 1; 501 data = malloc(SCARG(uap, len), M_TEMP, M_WAITOK|M_ZERO); 502 retp = (int *)&data[SCARG(uap, len) - sizeof(ret)]; 503 *retp = ret; 504 505 if ((error = copyout(data, SCARG(uap, mask), SCARG(uap, len))) != 0) 506 return error; 507 508 free(data, M_TEMP); 509 510 return 0; 511 512 } 513 514 int 515 linux_sys_sched_setaffinity(struct lwp *l, const struct linux_sys_sched_setaffinity_args *uap, register_t *retval) 516 { 517 /* { 518 syscallarg(pid_t) pid; 519 syscallarg(unsigned int) len; 520 syscallarg(unsigned long *) mask; 521 } */ 522 523 if (pfind(SCARG(uap, pid)) == NULL) 524 return ESRCH; 525 526 /* Let's ignore it */ 527 #ifdef DEBUG_LINUX 528 printf("linux_sys_sched_setaffinity\n"); 529 #endif 530 return 0; 531 }; 532 #endif /* LINUX_NPTL */ 533