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