]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/compat/linux/linux_emul.c
Group pid and parent are shared in a case of CLONE_THREAD not CLONE_VM.
[FreeBSD/FreeBSD.git] / sys / compat / linux / linux_emul.c
1 /*-
2  * Copyright (c) 2006 Roman Divacky
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer
10  *    in this position and unchanged.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include "opt_compat.h"
33
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/imgact.h>
37 #include <sys/lock.h>
38 #include <sys/malloc.h>
39 #include <sys/mutex.h>
40 #include <sys/sx.h>
41 #include <sys/proc.h>
42 #include <sys/syscallsubr.h>
43 #include <sys/sysproto.h>
44 #include <sys/unistd.h>
45
46 #include <compat/linux/linux_emul.h>
47 #include <compat/linux/linux_futex.h>
48
49 #ifdef COMPAT_LINUX32
50 #include <machine/../linux32/linux.h>
51 #include <machine/../linux32/linux32_proto.h>
52 #else
53 #include <machine/../linux/linux.h>
54 #include <machine/../linux/linux_proto.h>
55 #endif
56
57 struct sx emul_shared_lock;
58 struct sx emul_lock;
59
60 /* this returns locked reference to the emuldata entry (if found) */
61 struct linux_emuldata *
62 em_find(struct proc *p, int locked)
63 {
64         struct linux_emuldata *em;
65
66         if (locked == EMUL_UNLOCKED)
67                 EMUL_LOCK(&emul_lock);
68
69         em = p->p_emuldata;             
70
71         if (em == NULL && locked == EMUL_UNLOCKED)
72                 EMUL_UNLOCK(&emul_lock);
73
74         return (em);
75 }
76
77 int
78 linux_proc_init(struct thread *td, pid_t child, int flags)
79 {
80         struct linux_emuldata *em, *p_em;
81         struct proc *p;
82
83         if (child != 0) {
84                 /* non-exec call */
85                 em = malloc(sizeof *em, M_LINUX, M_WAITOK | M_ZERO);
86                 em->pid = child;
87                 em->pdeath_signal = 0;
88                 if (flags & CLONE_THREAD) {
89                         /* handled later in the code */
90                 } else {
91                         struct linux_emuldata_shared *s;
92
93                         s = malloc(sizeof *s, M_LINUX, M_WAITOK | M_ZERO);
94                         em->shared = s;
95                         s->refs = 1;
96                         s->group_pid = child;
97
98                         LIST_INIT(&s->threads);
99                 }
100                 p = pfind(child);
101                 KASSERT(p != NULL, ("process not found in proc_init\n"));
102                 p->p_emuldata = em;
103                 PROC_UNLOCK(p);
104                 EMUL_LOCK(&emul_lock);
105         } else {
106                 /* lookup the old one */
107                 em = em_find(td->td_proc, EMUL_UNLOCKED);
108                 KASSERT(em != NULL, ("proc_init: emuldata not found in exec case.\n"));
109         }
110
111         em->child_clear_tid = NULL;
112         em->child_set_tid = NULL;
113
114         /* 
115          * allocate the shared struct only in clone()/fork cases 
116          * in the case of clone() td = calling proc and child = pid of 
117          * the newly created proc
118          */
119         if (child != 0) {
120                 if (flags & CLONE_THREAD) {
121                         /* lookup the parent */
122                         p_em = em_find(td->td_proc, EMUL_LOCKED);
123                         KASSERT(p_em != NULL, ("proc_init: parent emuldata not found for CLONE_THREAD\n"));
124                         em->shared = p_em->shared;
125                         em->shared->refs++;
126                 } else {
127                         /* handled earlier to avoid malloc(M_WAITOK) with rwlock held */
128                 }
129         }
130
131
132         if (child != 0) {
133                 EMUL_UNLOCK(&emul_lock);
134                 EMUL_SHARED_WLOCK(&emul_shared_lock);
135                 LIST_INSERT_HEAD(&em->shared->threads, em, threads);
136                 EMUL_SHARED_WUNLOCK(&emul_shared_lock);
137
138                 p = pfind(child);
139                 /* we might have a sleeping linux_schedtail */
140                 wakeup(&p->p_emuldata);
141                 PROC_UNLOCK(p);
142         } else
143                 EMUL_UNLOCK(&emul_lock);
144
145         return (0);
146 }
147
148 void
149 linux_proc_exit(void *arg __unused, struct proc *p)
150 {
151         struct linux_emuldata *em;
152         int error;
153         struct thread *td = FIRST_THREAD_IN_PROC(p);
154         int *child_clear_tid;
155         struct proc *q, *nq;
156
157         if (__predict_true(p->p_sysent != &elf_linux_sysvec))
158                 return;
159
160         /* find the emuldata */
161         em = em_find(p, EMUL_UNLOCKED);
162
163         KASSERT(em != NULL, ("proc_exit: emuldata not found.\n"));
164
165         child_clear_tid = em->child_clear_tid;
166         
167         EMUL_UNLOCK(&emul_lock);
168
169         EMUL_SHARED_WLOCK(&emul_shared_lock);
170         LIST_REMOVE(em, threads);
171
172         PROC_LOCK(p);
173         p->p_emuldata = NULL;
174         PROC_UNLOCK(p);
175
176         em->shared->refs--;
177         if (em->shared->refs == 0)
178                 free(em->shared, M_LINUX);
179         EMUL_SHARED_WUNLOCK(&emul_shared_lock);
180
181         if (child_clear_tid != NULL) {
182                 struct linux_sys_futex_args cup;
183                 int null = 0;
184
185                 error = copyout(&null, child_clear_tid, sizeof(null));
186                 if (error) {
187                         free(em, M_LINUX);
188                         return;
189                 }
190
191                 /* futexes stuff */
192                 cup.uaddr = child_clear_tid;
193                 cup.op = LINUX_FUTEX_WAKE;
194                 cup.val = 0x7fffffff; /* Awake everyone */
195                 cup.timeout = NULL;
196                 cup.uaddr2 = NULL;
197                 cup.val3 = 0;
198                 error = linux_sys_futex(FIRST_THREAD_IN_PROC(p), &cup);
199                 /* 
200                  * this cannot happen at the moment and if this happens
201                  * it probably mean there is a userspace bug
202                  */
203                 if (error)
204                         printf(LMSG("futex stuff in proc_exit failed.\n"));
205         }
206
207         /* clean the stuff up */
208         free(em, M_LINUX);
209
210         /* this is a little weird but rewritten from exit1() */
211         sx_xlock(&proctree_lock);
212         q = LIST_FIRST(&p->p_children);
213         for (; q != NULL; q = nq) {
214                 nq = LIST_NEXT(q, p_sibling);
215                 if (q->p_flag & P_WEXIT)
216                         continue;
217                 if (__predict_false(q->p_sysent != &elf_linux_sysvec))
218                         continue;
219                 em = em_find(q, EMUL_UNLOCKED);
220                 KASSERT(em != NULL, ("linux_reparent: emuldata not found: %i\n", q->p_pid));
221                 if (em->pdeath_signal != 0) {
222                         PROC_LOCK(q);
223                         psignal(q, em->pdeath_signal);
224                         PROC_UNLOCK(q);
225                 }
226                 EMUL_UNLOCK(&emul_lock);
227         }
228         sx_xunlock(&proctree_lock);
229 }
230
231 /* 
232  * This is used in a case of transition from FreeBSD binary execing to linux binary
233  * in this case we create linux emuldata proc entry with the pid of the currently running
234  * process.
235  */
236 void linux_proc_exec(void *arg __unused, struct proc *p, struct image_params *imgp)
237 {
238         if (__predict_false(imgp->sysent == &elf_linux_sysvec 
239                  && p->p_sysent != &elf_linux_sysvec))
240                 linux_proc_init(FIRST_THREAD_IN_PROC(p), p->p_pid, 0);
241         if (__predict_false(imgp->sysent != &elf_linux_sysvec
242                  && p->p_sysent == &elf_linux_sysvec)) {
243                 struct linux_emuldata *em;
244
245                 em = em_find(p, EMUL_UNLOCKED);
246
247                 KASSERT(em != NULL, ("proc_exec: emuldata not found.\n"));
248                 
249                 EMUL_UNLOCK(&emul_lock);
250
251                 EMUL_SHARED_WLOCK(&emul_shared_lock);
252                 LIST_REMOVE(em, threads);
253
254                 PROC_LOCK(p);
255                 p->p_emuldata = NULL;
256                 PROC_UNLOCK(p);
257
258                 em->shared->refs--;
259                 if (em->shared->refs == 0)
260                         free(em->shared, M_LINUX);
261                 EMUL_SHARED_WUNLOCK(&emul_shared_lock);
262
263                 free(em, M_LINUX);
264         }
265 }
266
267 extern int hz;          /* in subr_param.c */
268
269 void
270 linux_schedtail(void *arg __unused, struct proc *p)
271 {
272         struct linux_emuldata *em;
273         int error = 0;
274         int *child_set_tid;
275
276         if (__predict_true(p->p_sysent != &elf_linux_sysvec))
277                 return;
278
279 retry:  
280         /* find the emuldata */
281         em = em_find(p, EMUL_UNLOCKED);
282
283         if (em == NULL) {
284                 /* 
285                  * We might have been called before proc_init for this process so
286                  * tsleep and be woken up by it. We use p->p_emuldata for this
287                  */
288
289                 error = tsleep(&p->p_emuldata, PLOCK, "linux_schedtail", hz);
290                 if (error == 0)
291                         goto retry;
292                 panic("no emuldata found for userreting process.\n");
293         }
294         child_set_tid = em->child_set_tid;
295         EMUL_UNLOCK(&emul_lock);
296
297         if (child_set_tid != NULL)
298                 error = copyout(&p->p_pid, (int *) child_set_tid, sizeof(p->p_pid));
299
300         return;
301 }
302
303 int
304 linux_set_tid_address(struct thread *td, struct linux_set_tid_address_args *args)
305 {
306         struct linux_emuldata *em;
307
308 #ifdef DEBUG
309         if (ldebug(set_tid_address))
310                 printf(ARGS(set_tid_address, "%p"), args->tidptr);
311 #endif
312
313          /* find the emuldata */
314         em = em_find(td->td_proc, EMUL_UNLOCKED);
315
316         KASSERT(em != NULL, ("set_tid_address: emuldata not found.\n"));
317
318         em->child_clear_tid = args->tidptr;
319         td->td_retval[0] = td->td_proc->p_pid;
320
321         EMUL_UNLOCK(&emul_lock);
322         return 0;
323 }