linux_emul.c (e7153b2583ec32ced588706fe1996d909b23bc3c) | linux_emul.c (ad2056f2c46cd6f28b2f7a4bd0e1a72198b19eee) |
---|---|
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 --- 20 unchanged lines hidden (view full) --- 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> | 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 --- 20 unchanged lines hidden (view full) --- 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/kernel.h> | |
38#include <sys/lock.h> 39#include <sys/malloc.h> 40#include <sys/mutex.h> 41#include <sys/sx.h> 42#include <sys/proc.h> 43#include <sys/syscallsubr.h> 44#include <sys/sysproto.h> 45#include <sys/unistd.h> 46 | 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 |
|
47#ifdef COMPAT_LINUX32 48#include <machine/../linux32/linux.h> 49#include <machine/../linux32/linux32_proto.h> 50#else 51#include <machine/../linux/linux.h> 52#include <machine/../linux/linux_proto.h> 53#endif 54 | 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 |
55#include <compat/linux/linux_emul.h> 56#include <compat/linux/linux_futex.h> | 57struct sx emul_shared_lock; 58struct sx emul_lock; |
57 | 59 |
58struct sx emul_shared_lock; 59struct mtx emul_lock; 60 | |
61/* this returns locked reference to the emuldata entry (if found) */ 62struct linux_emuldata * 63em_find(struct proc *p, int locked) 64{ 65 struct linux_emuldata *em; 66 | 60/* this returns locked reference to the emuldata entry (if found) */ 61struct linux_emuldata * 62em_find(struct proc *p, int locked) 63{ 64 struct linux_emuldata *em; 65 |
67 if (locked == EMUL_DOLOCK) 68 EMUL_LOCK(&emul_lock); | 66 if (locked == EMUL_UNLOCKED) 67 EMUL_LOCK(&emul_lock); |
69 | 68 |
70 em = p->p_emuldata; | 69 em = p->p_emuldata; |
71 | 70 |
72 if (em == NULL && locked == EMUL_DOLOCK) 73 EMUL_UNLOCK(&emul_lock); | 71 if (em == NULL && locked == EMUL_UNLOCKED) 72 EMUL_UNLOCK(&emul_lock); |
74 75 return (em); 76} 77 78int 79linux_proc_init(struct thread *td, pid_t child, int flags) 80{ 81 struct linux_emuldata *em, *p_em; 82 struct proc *p; 83 84 if (child != 0) { | 73 74 return (em); 75} 76 77int 78linux_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) { |
85 /* non-exec call */ 86 em = malloc(sizeof *em, M_LINUX, M_WAITOK | M_ZERO); | 84 /* non-exec call */ 85 MALLOC(em, struct linux_emuldata *, sizeof *em, M_LINUX, M_WAITOK | M_ZERO); |
87 em->pid = child; | 86 em->pid = child; |
88 em->pdeath_signal = 0; 89 em->used_requeue = 0; 90 em->robust_futexes = NULL; 91 if (flags & LINUX_CLONE_THREAD) { 92 /* handled later in the code */ | 87 if (flags & CLONE_VM) { 88 /* handled later in the code */ |
93 } else { | 89 } else { |
94 struct linux_emuldata_shared *s; | 90 struct linux_emuldata_shared *s; |
95 | 91 |
96 s = malloc(sizeof *s, M_LINUX, M_WAITOK | M_ZERO); | 92 MALLOC(s, struct linux_emuldata_shared *, sizeof *s, M_LINUX, M_WAITOK | M_ZERO); 93 em->shared = s; |
97 s->refs = 1; 98 s->group_pid = child; 99 100 LIST_INIT(&s->threads); | 94 s->refs = 1; 95 s->group_pid = child; 96 97 LIST_INIT(&s->threads); |
101 em->shared = s; | |
102 } | 98 } |
99 p = pfind(child); 100 if (p == NULL) 101 panic("process not found in proc_init\n"); 102 p->p_emuldata = em; 103 PROC_UNLOCK(p); |
|
103 } else { 104 /* lookup the old one */ | 104 } else { 105 /* lookup the old one */ |
105 em = em_find(td->td_proc, EMUL_DOLOCK); | 106 em = em_find(td->td_proc, EMUL_UNLOCKED); |
106 KASSERT(em != NULL, ("proc_init: emuldata not found in exec case.\n")); 107 } 108 109 em->child_clear_tid = NULL; 110 em->child_set_tid = NULL; 111 | 107 KASSERT(em != NULL, ("proc_init: emuldata not found in exec case.\n")); 108 } 109 110 em->child_clear_tid = NULL; 111 em->child_set_tid = NULL; 112 |
112 /* 113 * allocate the shared struct only in clone()/fork cases in the case 114 * of clone() td = calling proc and child = pid of the newly created 115 * proc | 113 /* allocate the shared struct only in clone()/fork cases 114 * in the case of clone() td = calling proc and child = pid of 115 * the newly created proc |
116 */ 117 if (child != 0) { | 116 */ 117 if (child != 0) { |
118 if (flags & LINUX_CLONE_THREAD) { 119 /* lookup the parent */ 120 /* 121 * we dont have to lock the p_em because 122 * its waiting for us in linux_clone so 123 * there is no chance of it changing the 124 * p_em->shared address 125 */ 126 p_em = em_find(td->td_proc, EMUL_DONTLOCK); 127 KASSERT(p_em != NULL, ("proc_init: parent emuldata not found for CLONE_THREAD\n")); | 118 if (flags & CLONE_VM) { 119 /* lookup the parent */ 120 p_em = em_find(td->td_proc, EMUL_LOCKED); 121 KASSERT(p_em != NULL, ("proc_init: parent emuldata not found for CLONE_VM\n")); |
128 em->shared = p_em->shared; | 122 em->shared = p_em->shared; |
129 EMUL_SHARED_WLOCK(&emul_shared_lock); | |
130 em->shared->refs++; | 123 em->shared->refs++; |
131 EMUL_SHARED_WUNLOCK(&emul_shared_lock); | |
132 } else { | 124 } else { |
133 /* 134 * handled earlier to avoid malloc(M_WAITOK) with 135 * rwlock held 136 */ | 125 /* handled earlier to avoid malloc(M_WAITOK) with rwlock held */ |
137 } 138 } | 126 } 127 } |
128 129 |
|
139 if (child != 0) { | 130 if (child != 0) { |
140 EMUL_SHARED_WLOCK(&emul_shared_lock); 141 LIST_INSERT_HEAD(&em->shared->threads, em, threads); 142 EMUL_SHARED_WUNLOCK(&emul_shared_lock); | 131 EMUL_SHARED_WLOCK(&emul_shared_lock); 132 LIST_INSERT_HEAD(&em->shared->threads, em, threads); 133 EMUL_SHARED_WUNLOCK(&emul_shared_lock); |
143 144 p = pfind(child); | 134 135 p = pfind(child); |
145 KASSERT(p != NULL, ("process not found in proc_init\n")); 146 p->p_emuldata = em; | |
147 PROC_UNLOCK(p); | 136 PROC_UNLOCK(p); |
137 /* we might have a sleeping linux_schedtail */ 138 wakeup(&p->p_emuldata); |
|
148 } else | 139 } else |
149 EMUL_UNLOCK(&emul_lock); | 140 EMUL_UNLOCK(&emul_lock); |
150 | 141 |
151 return (0); | 142 return (0); |
152} 153 154void 155linux_proc_exit(void *arg __unused, struct proc *p) 156{ | 143} 144 145void 146linux_proc_exit(void *arg __unused, struct proc *p) 147{ |
157 struct linux_emuldata *em; | 148 struct linux_emuldata *em; |
158 int error; 159 struct thread *td = FIRST_THREAD_IN_PROC(p); 160 int *child_clear_tid; | 149 int error; 150 struct thread *td = FIRST_THREAD_IN_PROC(p); 151 int *child_clear_tid; |
161 struct proc *q, *nq; | |
162 163 if (__predict_true(p->p_sysent != &elf_linux_sysvec)) | 152 153 if (__predict_true(p->p_sysent != &elf_linux_sysvec)) |
164 return; | 154 return; |
165 | 155 |
166 release_futexes(p); 167 | |
168 /* find the emuldata */ | 156 /* find the emuldata */ |
169 em = em_find(p, EMUL_DOLOCK); | 157 em = em_find(p, EMUL_UNLOCKED); |
170 171 KASSERT(em != NULL, ("proc_exit: emuldata not found.\n")); 172 | 158 159 KASSERT(em != NULL, ("proc_exit: emuldata not found.\n")); 160 |
173 /* reparent all procs that are not a thread leader to initproc */ 174 if (em->shared->group_pid != p->p_pid) { 175 child_clear_tid = em->child_clear_tid; 176 EMUL_UNLOCK(&emul_lock); 177 sx_xlock(&proctree_lock); 178 wakeup(initproc); 179 PROC_LOCK(p); 180 proc_reparent(p, initproc); 181 p->p_sigparent = SIGCHLD; 182 PROC_UNLOCK(p); 183 sx_xunlock(&proctree_lock); 184 } else { 185 child_clear_tid = em->child_clear_tid; 186 EMUL_UNLOCK(&emul_lock); 187 } | 161 child_clear_tid = em->child_clear_tid; 162 163 EMUL_UNLOCK(&emul_lock); |
188 189 EMUL_SHARED_WLOCK(&emul_shared_lock); 190 LIST_REMOVE(em, threads); 191 | 164 165 EMUL_SHARED_WLOCK(&emul_shared_lock); 166 LIST_REMOVE(em, threads); 167 |
168 PROC_LOCK(p); 169 p->p_emuldata = NULL; 170 PROC_UNLOCK(p); 171 |
|
192 em->shared->refs--; | 172 em->shared->refs--; |
193 if (em->shared->refs == 0) { 194 EMUL_SHARED_WUNLOCK(&emul_shared_lock); 195 free(em->shared, M_LINUX); 196 } else 197 EMUL_SHARED_WUNLOCK(&emul_shared_lock); | 173 if (em->shared->refs == 0) 174 FREE(em->shared, M_LINUX); 175 EMUL_SHARED_WUNLOCK(&emul_shared_lock); |
198 199 if (child_clear_tid != NULL) { | 176 177 if (child_clear_tid != NULL) { |
200 struct linux_sys_futex_args cup; | 178 struct linux_sys_futex_args cup; |
201 int null = 0; 202 203 error = copyout(&null, child_clear_tid, sizeof(null)); | 179 int null = 0; 180 181 error = copyout(&null, child_clear_tid, sizeof(null)); |
204 if (error) { 205 free(em, M_LINUX); 206 return; 207 } | 182 if (error) 183 return; |
208 209 /* futexes stuff */ 210 cup.uaddr = child_clear_tid; 211 cup.op = LINUX_FUTEX_WAKE; | 184 185 /* futexes stuff */ 186 cup.uaddr = child_clear_tid; 187 cup.op = LINUX_FUTEX_WAKE; |
212 cup.val = 0x7fffffff; /* Awake everyone */ | 188 cup.val = 0x7fffffff; /* Awake everyone */ |
213 cup.timeout = NULL; 214 cup.uaddr2 = NULL; 215 cup.val3 = 0; 216 error = linux_sys_futex(FIRST_THREAD_IN_PROC(p), &cup); | 189 cup.timeout = NULL; 190 cup.uaddr2 = NULL; 191 cup.val3 = 0; 192 error = linux_sys_futex(FIRST_THREAD_IN_PROC(p), &cup); |
217 /* 218 * this cannot happen at the moment and if this happens it 219 * probably means there is a user space bug 220 */ | 193 /* this cannot happen at the moment and if this happens 194 * it probably mean there is a userspace bug 195 */ |
221 if (error) | 196 if (error) |
222 printf(LMSG("futex stuff in proc_exit failed.\n")); | 197 printf(LMSG("futex stuff in proc_exit failed.\n")); |
223 } 224 225 /* clean the stuff up */ | 198 } 199 200 /* clean the stuff up */ |
226 free(em, M_LINUX); 227 228 /* this is a little weird but rewritten from exit1() */ 229 sx_xlock(&proctree_lock); 230 q = LIST_FIRST(&p->p_children); 231 for (; q != NULL; q = nq) { 232 nq = LIST_NEXT(q, p_sibling); 233 if (q->p_flag & P_WEXIT) 234 continue; 235 if (__predict_false(q->p_sysent != &elf_linux_sysvec)) 236 continue; 237 em = em_find(q, EMUL_DOLOCK); 238 KASSERT(em != NULL, ("linux_reparent: emuldata not found: %i\n", q->p_pid)); 239 PROC_LOCK(q); 240 if ((q->p_flag & P_WEXIT) == 0 && em->pdeath_signal != 0) { 241 psignal(q, em->pdeath_signal); 242 } 243 PROC_UNLOCK(q); 244 EMUL_UNLOCK(&emul_lock); 245 } 246 sx_xunlock(&proctree_lock); | 201 FREE(em, M_LINUX); |
247} 248 | 202} 203 |
249/* 250 * This is used in a case of transition from FreeBSD binary execing to linux binary | 204/* This is used in a case of transition from FreeBSD binary execing to linux binary |
251 * in this case we create linux emuldata proc entry with the pid of the currently running 252 * process. 253 */ | 205 * in this case we create linux emuldata proc entry with the pid of the currently running 206 * process. 207 */ |
254void 255linux_proc_exec(void *arg __unused, struct proc *p, struct image_params *imgp) | 208void linux_proc_exec(void *arg __unused, struct proc *p, struct image_params *imgp) |
256{ | 209{ |
257 if (__predict_false(imgp->sysent == &elf_linux_sysvec 258 && p->p_sysent != &elf_linux_sysvec)) 259 linux_proc_init(FIRST_THREAD_IN_PROC(p), p->p_pid, 0); | 210 if (__predict_false(imgp->sysent == &elf_linux_sysvec 211 && p->p_sysent != &elf_linux_sysvec)) 212 linux_proc_init(FIRST_THREAD_IN_PROC(p), p->p_pid, 0); |
260 if (__predict_false(imgp->sysent != &elf_linux_sysvec | 213 if (__predict_false(imgp->sysent != &elf_linux_sysvec |
261 && p->p_sysent == &elf_linux_sysvec)) { 262 struct linux_emuldata *em; | 214 && p->p_sysent == &elf_linux_sysvec)) { 215 struct linux_emuldata *em; |
263 | 216 |
264 /* 265 * XXX:There's a race because here we assign p->p_emuldata NULL 266 * but the process is still counted as linux one for a short 267 * time so some other process might reference it and try to 268 * access its p->p_emuldata and panicing on a NULL reference. 269 */ 270 em = em_find(p, EMUL_DONTLOCK); | 217 em = em_find(p, EMUL_UNLOCKED); |
271 272 KASSERT(em != NULL, ("proc_exec: emuldata not found.\n")); | 218 219 KASSERT(em != NULL, ("proc_exec: emuldata not found.\n")); |
220 221 EMUL_UNLOCK(&emul_lock); |
|
273 274 EMUL_SHARED_WLOCK(&emul_shared_lock); 275 LIST_REMOVE(em, threads); 276 277 PROC_LOCK(p); 278 p->p_emuldata = NULL; 279 PROC_UNLOCK(p); 280 281 em->shared->refs--; | 222 223 EMUL_SHARED_WLOCK(&emul_shared_lock); 224 LIST_REMOVE(em, threads); 225 226 PROC_LOCK(p); 227 p->p_emuldata = NULL; 228 PROC_UNLOCK(p); 229 230 em->shared->refs--; |
282 if (em->shared->refs == 0) { 283 EMUL_SHARED_WUNLOCK(&emul_shared_lock); 284 free(em->shared, M_LINUX); 285 } else 286 EMUL_SHARED_WUNLOCK(&emul_shared_lock); | 231 if (em->shared->refs == 0) 232 FREE(em->shared, M_LINUX); 233 EMUL_SHARED_WUNLOCK(&emul_shared_lock); |
287 | 234 |
288 free(em, M_LINUX); | 235 FREE(em, M_LINUX); |
289 } 290} 291 | 236 } 237} 238 |
239extern int hz; /* in subr_param.c */ 240 |
|
292void 293linux_schedtail(void *arg __unused, struct proc *p) 294{ 295 struct linux_emuldata *em; 296 int error = 0; | 241void 242linux_schedtail(void *arg __unused, struct proc *p) 243{ 244 struct linux_emuldata *em; 245 int error = 0; |
246#ifdef DEBUG 247 struct thread *td = FIRST_THREAD_IN_PROC(p); 248#endif |
|
297 int *child_set_tid; 298 | 249 int *child_set_tid; 250 |
299 if (__predict_true(p->p_sysent != &elf_linux_sysvec)) 300 return; | 251 if (p->p_sysent != &elf_linux_sysvec) 252 return; |
301 | 253 |
254retry: |
|
302 /* find the emuldata */ | 255 /* find the emuldata */ |
303 em = em_find(p, EMUL_DOLOCK); | 256 em = em_find(p, EMUL_UNLOCKED); |
304 | 257 |
305 KASSERT(em != NULL, ("linux_schedtail: emuldata not found.\n")); | 258 if (em == NULL) { 259 /* We might have been called before proc_init for this process so 260 * tsleep and be woken up by it. We use p->p_emuldata for this 261 */ 262 263 error = tsleep(&p->p_emuldata, PLOCK, "linux_schedtail", hz); 264 if (error == 0) 265 goto retry; 266 panic("no emuldata found for userreting process.\n"); 267 } |
306 child_set_tid = em->child_set_tid; 307 EMUL_UNLOCK(&emul_lock); 308 309 if (child_set_tid != NULL) | 268 child_set_tid = em->child_set_tid; 269 EMUL_UNLOCK(&emul_lock); 270 271 if (child_set_tid != NULL) |
310 error = copyout(&p->p_pid, (int *)child_set_tid, 311 sizeof(p->p_pid)); | 272 error = copyout(&p->p_pid, (int *) child_set_tid, sizeof(p->p_pid)); |
312 313 return; 314} 315 316int 317linux_set_tid_address(struct thread *td, struct linux_set_tid_address_args *args) 318{ | 273 274 return; 275} 276 277int 278linux_set_tid_address(struct thread *td, struct linux_set_tid_address_args *args) 279{ |
319 struct linux_emuldata *em; | 280 struct linux_emuldata *em; |
320 321#ifdef DEBUG 322 if (ldebug(set_tid_address)) 323 printf(ARGS(set_tid_address, "%p"), args->tidptr); 324#endif 325 | 281 282#ifdef DEBUG 283 if (ldebug(set_tid_address)) 284 printf(ARGS(set_tid_address, "%p"), args->tidptr); 285#endif 286 |
326 /* find the emuldata */ 327 em = em_find(td->td_proc, EMUL_DOLOCK); | 287 /* find the emuldata */ 288 em = em_find(td->td_proc, EMUL_UNLOCKED); |
328 329 KASSERT(em != NULL, ("set_tid_address: emuldata not found.\n")); 330 331 em->child_clear_tid = args->tidptr; 332 td->td_retval[0] = td->td_proc->p_pid; 333 334 EMUL_UNLOCK(&emul_lock); 335 return 0; 336} | 289 290 KASSERT(em != NULL, ("set_tid_address: emuldata not found.\n")); 291 292 em->child_clear_tid = args->tidptr; 293 td->td_retval[0] = td->td_proc->p_pid; 294 295 EMUL_UNLOCK(&emul_lock); 296 return 0; 297} |