linux_sched.c revision 1.24 1 /* $NetBSD: linux_sched.c,v 1.24 2005/11/09 14:56:50 manu 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.24 2005/11/09 14:56:50 manu 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/savar.h>
55 #include <sys/syscallargs.h>
56 #include <sys/wait.h>
57
58 #include <machine/cpu.h>
59
60 #include <compat/linux/common/linux_types.h>
61 #include <compat/linux/common/linux_exec.h>
62 #include <compat/linux/common/linux_signal.h>
63 #include <compat/linux/common/linux_machdep.h> /* For LINUX_NPTL */
64 #include <compat/linux/common/linux_emuldata.h>
65
66 #include <compat/linux/linux_syscallargs.h>
67
68 #include <compat/linux/common/linux_sched.h>
69
70 int
71 linux_sys_clone(l, v, retval)
72 struct lwp *l;
73 void *v;
74 register_t *retval;
75 {
76 struct linux_sys_clone_args /* {
77 syscallarg(int) flags;
78 syscallarg(void *) stack;
79 #ifdef LINUX_NPTL
80 syscallarg(void *) parent_tidptr;
81 syscallarg(void *) child_tidptr;
82 #endif
83 } */ *uap = v;
84 int flags, sig;
85 int error;
86 #ifdef LINUX_NPTL
87 struct linux_emuldata *led;
88 #endif
89
90 /*
91 * We don't support the Linux CLONE_PID or CLONE_PTRACE flags.
92 */
93 if (SCARG(uap, flags) & (LINUX_CLONE_PID|LINUX_CLONE_PTRACE))
94 return (EINVAL);
95
96 /*
97 * Thread group implies shared signals. Shared signals
98 * imply shared VM. This matches what Linux kernel does.
99 */
100 if (SCARG(uap, flags) & LINUX_CLONE_THREAD
101 && (SCARG(uap, flags) & LINUX_CLONE_SIGHAND) == 0)
102 return (EINVAL);
103 if (SCARG(uap, flags) & LINUX_CLONE_SIGHAND
104 && (SCARG(uap, flags) & LINUX_CLONE_VM) == 0)
105 return (EINVAL);
106
107 flags = 0;
108
109 if (SCARG(uap, flags) & LINUX_CLONE_VM)
110 flags |= FORK_SHAREVM;
111 if (SCARG(uap, flags) & LINUX_CLONE_FS)
112 flags |= FORK_SHARECWD;
113 if (SCARG(uap, flags) & LINUX_CLONE_FILES)
114 flags |= FORK_SHAREFILES;
115 if (SCARG(uap, flags) & LINUX_CLONE_SIGHAND)
116 flags |= FORK_SHARESIGS;
117 if (SCARG(uap, flags) & LINUX_CLONE_VFORK)
118 flags |= FORK_PPWAIT;
119
120 sig = SCARG(uap, flags) & LINUX_CLONE_CSIGNAL;
121 if (sig < 0 || sig >= LINUX__NSIG)
122 return (EINVAL);
123 sig = linux_to_native_signo[sig];
124
125 #ifdef LINUX_NPTL
126 led = (struct linux_emuldata *)l->l_proc->p_emuldata;
127
128 if (SCARG(uap, flags) & LINUX_CLONE_PARENT_SETTID) {
129 if (SCARG(uap, parent_tidptr) == NULL) {
130 printf("linux_sys_clone: NULL parent_tidptr\n");
131 return EINVAL;
132 }
133
134 if ((error = copyout(&l->l_proc->p_pid,
135 SCARG(uap, parent_tidptr),
136 sizeof(l->l_proc->p_pid))) != 0)
137 return error;
138 }
139
140 /* CLONE_CHILD_CLEARTID: TID clear in the child on exit() */
141 if (SCARG(uap, flags) & LINUX_CLONE_CHILD_CLEARTID)
142 led->child_clear_tid = SCARG(uap, child_tidptr);
143 else
144 led->child_clear_tid = NULL;
145
146 /* CLONE_CHILD_SETTID: TID set in the child on clone() */
147 if (SCARG(uap, flags) & LINUX_CLONE_CHILD_SETTID)
148 led->child_set_tid = SCARG(uap, child_tidptr);
149 else
150 led->child_set_tid = NULL;
151
152 /* CLONE_SETTLS: new Thread Local Storage in the child */
153 if (SCARG(uap, flags) & LINUX_CLONE_SETTLS)
154 led->set_tls = linux_get_newtls(l);
155 else
156 led->set_tls = 0;
157 #endif /* LINUX_NPTL */
158 /*
159 * Note that Linux does not provide a portable way of specifying
160 * the stack area; the caller must know if the stack grows up
161 * or down. So, we pass a stack size of 0, so that the code
162 * that makes this adjustment is a noop.
163 */
164 if ((error = fork1(l, flags, sig, SCARG(uap, stack), 0,
165 NULL, NULL, retval, NULL)) != 0)
166 return error;
167
168 return 0;
169 }
170
171 int
172 linux_sys_sched_setparam(cl, v, retval)
173 struct lwp *cl;
174 void *v;
175 register_t *retval;
176 {
177 struct linux_sys_sched_setparam_args /* {
178 syscallarg(linux_pid_t) pid;
179 syscallarg(const struct linux_sched_param *) sp;
180 } */ *uap = v;
181 struct proc *cp = cl->l_proc;
182 int error;
183 struct linux_sched_param lp;
184 struct proc *p;
185
186 /*
187 * We only check for valid parameters and return afterwards.
188 */
189
190 if (SCARG(uap, pid) < 0 || SCARG(uap, sp) == NULL)
191 return EINVAL;
192
193 error = copyin(SCARG(uap, sp), &lp, sizeof(lp));
194 if (error)
195 return error;
196
197 if (SCARG(uap, pid) != 0) {
198 struct pcred *pc = cp->p_cred;
199
200 if ((p = pfind(SCARG(uap, pid))) == NULL)
201 return ESRCH;
202 if (!(cp == p ||
203 pc->pc_ucred->cr_uid == 0 ||
204 pc->p_ruid == p->p_cred->p_ruid ||
205 pc->pc_ucred->cr_uid == p->p_cred->p_ruid ||
206 pc->p_ruid == p->p_ucred->cr_uid ||
207 pc->pc_ucred->cr_uid == p->p_ucred->cr_uid))
208 return EPERM;
209 }
210
211 return 0;
212 }
213
214 int
215 linux_sys_sched_getparam(cl, v, retval)
216 struct lwp *cl;
217 void *v;
218 register_t *retval;
219 {
220 struct linux_sys_sched_getparam_args /* {
221 syscallarg(linux_pid_t) pid;
222 syscallarg(struct linux_sched_param *) sp;
223 } */ *uap = v;
224 struct proc *cp = cl->l_proc;
225 struct proc *p;
226 struct linux_sched_param lp;
227
228 /*
229 * We only check for valid parameters and return a dummy priority afterwards.
230 */
231 if (SCARG(uap, pid) < 0 || SCARG(uap, sp) == NULL)
232 return EINVAL;
233
234 if (SCARG(uap, pid) != 0) {
235 struct pcred *pc = cp->p_cred;
236
237 if ((p = pfind(SCARG(uap, pid))) == NULL)
238 return ESRCH;
239 if (!(cp == p ||
240 pc->pc_ucred->cr_uid == 0 ||
241 pc->p_ruid == p->p_cred->p_ruid ||
242 pc->pc_ucred->cr_uid == p->p_cred->p_ruid ||
243 pc->p_ruid == p->p_ucred->cr_uid ||
244 pc->pc_ucred->cr_uid == p->p_ucred->cr_uid))
245 return EPERM;
246 }
247
248 lp.sched_priority = 0;
249 return copyout(&lp, SCARG(uap, sp), sizeof(lp));
250 }
251
252 int
253 linux_sys_sched_setscheduler(cl, v, retval)
254 struct lwp *cl;
255 void *v;
256 register_t *retval;
257 {
258 struct linux_sys_sched_setscheduler_args /* {
259 syscallarg(linux_pid_t) pid;
260 syscallarg(int) policy;
261 syscallarg(cont struct linux_sched_scheduler *) sp;
262 } */ *uap = v;
263 struct proc *cp = cl->l_proc;
264 int error;
265 struct linux_sched_param lp;
266 struct proc *p;
267
268 /*
269 * We only check for valid parameters and return afterwards.
270 */
271
272 if (SCARG(uap, pid) < 0 || SCARG(uap, sp) == NULL)
273 return EINVAL;
274
275 error = copyin(SCARG(uap, sp), &lp, sizeof(lp));
276 if (error)
277 return error;
278
279 if (SCARG(uap, pid) != 0) {
280 struct pcred *pc = cp->p_cred;
281
282 if ((p = pfind(SCARG(uap, pid))) == NULL)
283 return ESRCH;
284 if (!(cp == p ||
285 pc->pc_ucred->cr_uid == 0 ||
286 pc->p_ruid == p->p_cred->p_ruid ||
287 pc->pc_ucred->cr_uid == p->p_cred->p_ruid ||
288 pc->p_ruid == p->p_ucred->cr_uid ||
289 pc->pc_ucred->cr_uid == p->p_ucred->cr_uid))
290 return EPERM;
291 }
292
293 /*
294 * We can't emulate anything put the default scheduling policy.
295 */
296 if (SCARG(uap, policy) != LINUX_SCHED_OTHER || lp.sched_priority != 0)
297 return EINVAL;
298
299 return 0;
300 }
301
302 int
303 linux_sys_sched_getscheduler(cl, v, retval)
304 struct lwp *cl;
305 void *v;
306 register_t *retval;
307 {
308 struct linux_sys_sched_getscheduler_args /* {
309 syscallarg(linux_pid_t) pid;
310 } */ *uap = v;
311 struct proc *cp = cl->l_proc;
312 struct proc *p;
313
314 *retval = -1;
315 /*
316 * We only check for valid parameters and return afterwards.
317 */
318
319 if (SCARG(uap, pid) != 0) {
320 struct pcred *pc = cp->p_cred;
321
322 if ((p = pfind(SCARG(uap, pid))) == NULL)
323 return ESRCH;
324 if (!(cp == p ||
325 pc->pc_ucred->cr_uid == 0 ||
326 pc->p_ruid == p->p_cred->p_ruid ||
327 pc->pc_ucred->cr_uid == p->p_cred->p_ruid ||
328 pc->p_ruid == p->p_ucred->cr_uid ||
329 pc->pc_ucred->cr_uid == p->p_ucred->cr_uid))
330 return EPERM;
331 }
332
333 /*
334 * We can't emulate anything put the default scheduling policy.
335 */
336 *retval = LINUX_SCHED_OTHER;
337 return 0;
338 }
339
340 int
341 linux_sys_sched_yield(cl, v, retval)
342 struct lwp *cl;
343 void *v;
344 register_t *retval;
345 {
346
347 yield();
348 return 0;
349 }
350
351 int
352 linux_sys_sched_get_priority_max(cl, v, retval)
353 struct lwp *cl;
354 void *v;
355 register_t *retval;
356 {
357 struct linux_sys_sched_get_priority_max_args /* {
358 syscallarg(int) policy;
359 } */ *uap = v;
360
361 /*
362 * We can't emulate anything put the default scheduling policy.
363 */
364 if (SCARG(uap, policy) != LINUX_SCHED_OTHER) {
365 *retval = -1;
366 return EINVAL;
367 }
368
369 *retval = 0;
370 return 0;
371 }
372
373 int
374 linux_sys_sched_get_priority_min(cl, v, retval)
375 struct lwp *cl;
376 void *v;
377 register_t *retval;
378 {
379 struct linux_sys_sched_get_priority_min_args /* {
380 syscallarg(int) policy;
381 } */ *uap = v;
382
383 /*
384 * We can't emulate anything put the default scheduling policy.
385 */
386 if (SCARG(uap, policy) != LINUX_SCHED_OTHER) {
387 *retval = -1;
388 return EINVAL;
389 }
390
391 *retval = 0;
392 return 0;
393 }
394
395 #ifndef __m68k__
396 /* Present on everything but m68k */
397 int
398 linux_sys_exit_group(l, v, retval)
399 struct lwp *l;
400 void *v;
401 register_t *retval;
402 {
403 struct linux_sys_exit_group_args /* {
404 syscallarg(int) error_code;
405 } */ *uap = v;
406 struct proc *p;
407 struct linux_emuldata *led;
408 pid_t group_pid;
409
410 /*
411 * 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).
414 */
415 PROCLIST_FOREACH(p, &allproc) {
416 if (p->p_emul != &emul_linux)
417 continue;
418
419 if (p == l->l_proc)
420 continue;
421
422 led = p->p_emuldata;
423 if (led->s->group_pid == group_pid) {
424 /* XXX we should exit, not send a SIGKILL */
425 psignal(p, SIGKILL)
426 }
427 }
428
429 exit1(l, W_EXITCODE(SCARG(uap, error_code), 0));
430 /* NOTREACHED */
431 return 0;
432 }
433 #endif /* !__m68k__ */
434
435 #ifdef LINUX_NPTL
436 int
437 linux_sys_set_tid_address(l, v, retval)
438 struct lwp *l;
439 void *v;
440 register_t *retval;
441 {
442 struct linux_sys_set_tid_address_args /* {
443 syscallarg(int *) tidptr;
444 } */ *uap = v;
445 struct linux_emuldata *led;
446
447 led = (struct linux_emuldata *)l->l_proc->p_emuldata;
448 led->clear_tid = SCARG(uap, tid);
449
450 *retval = l->l_proc->p_pid;
451
452 return 0;
453 }
454
455 /* ARGUSED1 */
456 int
457 linux_sys_gettid(l, v, retval)
458 struct lwp *l;
459 void *v;
460 register_t *retval;
461 {
462 *retval = l->l_proc->p_pid;
463 return 0;
464 }
465
466 int
467 linux_sys_sched_getaffinity(l, v, retval)
468 struct lwp *l;
469 void *v;
470 register_t *retval;
471 {
472 struct linux_sys_sched_getaffinity_args /* {
473 syscallarg(pid_t) pid;
474 syscallarg(unsigned int) len;
475 syscallarg(unsigned long *) mask;
476 } */ *uap = v;
477 int error;
478 int ret;
479 int ncpu;
480 int name[2];
481 size_t sz;
482 char *data;
483 int *retp;
484
485 if (SCARG(uap, mask) == NULL)
486 return EINVAL;
487
488 if (SCARG(uap, len) < sizeof(int))
489 return EINVAL;
490
491 if (pfind(SCARG(uap, pid)) == NULL)
492 return ESRCH;
493
494 /*
495 * return the actual number of CPU, tag all of them as available
496 * The result is a mask, the first CPU being in the least significant
497 * bit.
498 */
499 name[0] = CTL_HW;
500 name[1] = HW_NCPU;
501 sz = sizeof(ncpu);
502
503 if ((error = old_sysctl(&name[0], 2, &ncpu, &sz, NULL, 0, NULL)) != 0)
504 return error;
505
506 ret = (1 << ncpu) - 1;
507
508 data = malloc(SCARG(uap, len), M_TEMP, M_WAITOK|M_ZERO);
509 retp = (int *)&data[SCARG(uap, len) - sizeof(ret)];
510 *retp = ret;
511
512 if ((error = copyout(data, SCARG(uap, mask), SCARG(uap, len))) != 0)
513 return error;
514
515 free(data, M_TEMP);
516
517 return 0;
518
519 }
520
521 int
522 linux_sys_sched_setaffinity(l, v, retval)
523 struct lwp *l;
524 void *v;
525 register_t *retval;
526 {
527 struct linux_sys_sched_setaffinity_args /* {
528 syscallarg(pid_t) pid;
529 syscallarg(unsigned int) len;
530 syscallarg(unsigned long *) mask;
531 } */ *uap = v;
532
533 if (pfind(SCARG(uap, pid)) == NULL)
534 return ESRCH;
535
536 /* Let's ignore it */
537 #ifdef DEBUG_LINUX
538 printf("linux_sys_sched_setaffinity\n");
539 #endif
540 return 0;
541 };
542 #endif /* LINUX_NPTL */
543