Home | History | Annotate | Line # | Download | only in common
linux_sched.c revision 1.45
      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