Commit d597536303d762c4209cbab7e379819b8eb14536
1 parent
0a878c47
Multithreaded locking fixes.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@4692 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
10 changed files
with
447 additions
and
237 deletions
cpu-defs.h
... | ... | @@ -166,6 +166,7 @@ typedef struct CPUTLBEntry { |
166 | 166 | \ |
167 | 167 | void *next_cpu; /* next CPU sharing TB cache */ \ |
168 | 168 | int cpu_index; /* CPU index (informative) */ \ |
169 | + int running; /* Nonzero if cpu is currently running(usermode). */ \ | |
169 | 170 | /* user data */ \ |
170 | 171 | void *opaque; \ |
171 | 172 | \ | ... | ... |
cpu-exec.c
... | ... | @@ -44,7 +44,6 @@ |
44 | 44 | #endif |
45 | 45 | |
46 | 46 | int tb_invalidated_flag; |
47 | -static unsigned long next_tb; | |
48 | 47 | |
49 | 48 | //#define DEBUG_EXEC |
50 | 49 | //#define DEBUG_SIGNAL |
... | ... | @@ -93,8 +92,6 @@ static TranslationBlock *tb_find_slow(target_ulong pc, |
93 | 92 | target_ulong phys_pc, phys_page1, phys_page2, virt_page2; |
94 | 93 | uint8_t *tc_ptr; |
95 | 94 | |
96 | - spin_lock(&tb_lock); | |
97 | - | |
98 | 95 | tb_invalidated_flag = 0; |
99 | 96 | |
100 | 97 | regs_to_env(); /* XXX: do it just before cpu_gen_code() */ |
... | ... | @@ -155,7 +152,6 @@ static TranslationBlock *tb_find_slow(target_ulong pc, |
155 | 152 | found: |
156 | 153 | /* we add the TB in the virtual pc hash table */ |
157 | 154 | env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)] = tb; |
158 | - spin_unlock(&tb_lock); | |
159 | 155 | return tb; |
160 | 156 | } |
161 | 157 | |
... | ... | @@ -228,14 +224,6 @@ static inline TranslationBlock *tb_find_fast(void) |
228 | 224 | if (__builtin_expect(!tb || tb->pc != pc || tb->cs_base != cs_base || |
229 | 225 | tb->flags != flags, 0)) { |
230 | 226 | tb = tb_find_slow(pc, cs_base, flags); |
231 | - /* Note: we do it here to avoid a gcc bug on Mac OS X when | |
232 | - doing it in tb_find_slow */ | |
233 | - if (tb_invalidated_flag) { | |
234 | - /* as some TB could have been invalidated because | |
235 | - of memory exceptions while generating the code, we | |
236 | - must recompute the hash index here */ | |
237 | - next_tb = 0; | |
238 | - } | |
239 | 227 | } |
240 | 228 | return tb; |
241 | 229 | } |
... | ... | @@ -249,6 +237,7 @@ int cpu_exec(CPUState *env1) |
249 | 237 | int ret, interrupt_request; |
250 | 238 | TranslationBlock *tb; |
251 | 239 | uint8_t *tc_ptr; |
240 | + unsigned long next_tb; | |
252 | 241 | |
253 | 242 | if (cpu_halted(env1) == EXCP_HALTED) |
254 | 243 | return EXCP_HALTED; |
... | ... | @@ -577,7 +566,16 @@ int cpu_exec(CPUState *env1) |
577 | 566 | #endif |
578 | 567 | } |
579 | 568 | #endif |
569 | + spin_lock(&tb_lock); | |
580 | 570 | tb = tb_find_fast(); |
571 | + /* Note: we do it here to avoid a gcc bug on Mac OS X when | |
572 | + doing it in tb_find_slow */ | |
573 | + if (tb_invalidated_flag) { | |
574 | + /* as some TB could have been invalidated because | |
575 | + of memory exceptions while generating the code, we | |
576 | + must recompute the hash index here */ | |
577 | + next_tb = 0; | |
578 | + } | |
581 | 579 | #ifdef DEBUG_EXEC |
582 | 580 | if ((loglevel & CPU_LOG_EXEC)) { |
583 | 581 | fprintf(logfile, "Trace 0x%08lx [" TARGET_FMT_lx "] %s\n", |
... | ... | @@ -594,11 +592,10 @@ int cpu_exec(CPUState *env1) |
594 | 592 | (env->kqemu_enabled != 2) && |
595 | 593 | #endif |
596 | 594 | tb->page_addr[1] == -1) { |
597 | - spin_lock(&tb_lock); | |
598 | 595 | tb_add_jump((TranslationBlock *)(next_tb & ~3), next_tb & 3, tb); |
599 | - spin_unlock(&tb_lock); | |
600 | 596 | } |
601 | 597 | } |
598 | + spin_unlock(&tb_lock); | |
602 | 599 | tc_ptr = tb->tc_ptr; |
603 | 600 | env->current_tb = tb; |
604 | 601 | /* execute the generated code */ | ... | ... |
exec-all.h
... | ... | @@ -302,217 +302,7 @@ extern CPUWriteMemoryFunc *io_mem_write[IO_MEM_NB_ENTRIES][4]; |
302 | 302 | extern CPUReadMemoryFunc *io_mem_read[IO_MEM_NB_ENTRIES][4]; |
303 | 303 | extern void *io_mem_opaque[IO_MEM_NB_ENTRIES]; |
304 | 304 | |
305 | -#if defined(__hppa__) | |
306 | - | |
307 | -typedef int spinlock_t[4]; | |
308 | - | |
309 | -#define SPIN_LOCK_UNLOCKED { 1, 1, 1, 1 } | |
310 | - | |
311 | -static inline void resetlock (spinlock_t *p) | |
312 | -{ | |
313 | - (*p)[0] = (*p)[1] = (*p)[2] = (*p)[3] = 1; | |
314 | -} | |
315 | - | |
316 | -#else | |
317 | - | |
318 | -typedef int spinlock_t; | |
319 | - | |
320 | -#define SPIN_LOCK_UNLOCKED 0 | |
321 | - | |
322 | -static inline void resetlock (spinlock_t *p) | |
323 | -{ | |
324 | - *p = SPIN_LOCK_UNLOCKED; | |
325 | -} | |
326 | - | |
327 | -#endif | |
328 | - | |
329 | -#if defined(__powerpc__) | |
330 | -static inline int testandset (int *p) | |
331 | -{ | |
332 | - int ret; | |
333 | - __asm__ __volatile__ ( | |
334 | - "0: lwarx %0,0,%1\n" | |
335 | - " xor. %0,%3,%0\n" | |
336 | - " bne 1f\n" | |
337 | - " stwcx. %2,0,%1\n" | |
338 | - " bne- 0b\n" | |
339 | - "1: " | |
340 | - : "=&r" (ret) | |
341 | - : "r" (p), "r" (1), "r" (0) | |
342 | - : "cr0", "memory"); | |
343 | - return ret; | |
344 | -} | |
345 | -#elif defined(__i386__) | |
346 | -static inline int testandset (int *p) | |
347 | -{ | |
348 | - long int readval = 0; | |
349 | - | |
350 | - __asm__ __volatile__ ("lock; cmpxchgl %2, %0" | |
351 | - : "+m" (*p), "+a" (readval) | |
352 | - : "r" (1) | |
353 | - : "cc"); | |
354 | - return readval; | |
355 | -} | |
356 | -#elif defined(__x86_64__) | |
357 | -static inline int testandset (int *p) | |
358 | -{ | |
359 | - long int readval = 0; | |
360 | - | |
361 | - __asm__ __volatile__ ("lock; cmpxchgl %2, %0" | |
362 | - : "+m" (*p), "+a" (readval) | |
363 | - : "r" (1) | |
364 | - : "cc"); | |
365 | - return readval; | |
366 | -} | |
367 | -#elif defined(__s390__) | |
368 | -static inline int testandset (int *p) | |
369 | -{ | |
370 | - int ret; | |
371 | - | |
372 | - __asm__ __volatile__ ("0: cs %0,%1,0(%2)\n" | |
373 | - " jl 0b" | |
374 | - : "=&d" (ret) | |
375 | - : "r" (1), "a" (p), "0" (*p) | |
376 | - : "cc", "memory" ); | |
377 | - return ret; | |
378 | -} | |
379 | -#elif defined(__alpha__) | |
380 | -static inline int testandset (int *p) | |
381 | -{ | |
382 | - int ret; | |
383 | - unsigned long one; | |
384 | - | |
385 | - __asm__ __volatile__ ("0: mov 1,%2\n" | |
386 | - " ldl_l %0,%1\n" | |
387 | - " stl_c %2,%1\n" | |
388 | - " beq %2,1f\n" | |
389 | - ".subsection 2\n" | |
390 | - "1: br 0b\n" | |
391 | - ".previous" | |
392 | - : "=r" (ret), "=m" (*p), "=r" (one) | |
393 | - : "m" (*p)); | |
394 | - return ret; | |
395 | -} | |
396 | -#elif defined(__sparc__) | |
397 | -static inline int testandset (int *p) | |
398 | -{ | |
399 | - int ret; | |
400 | - | |
401 | - __asm__ __volatile__("ldstub [%1], %0" | |
402 | - : "=r" (ret) | |
403 | - : "r" (p) | |
404 | - : "memory"); | |
405 | - | |
406 | - return (ret ? 1 : 0); | |
407 | -} | |
408 | -#elif defined(__arm__) | |
409 | -static inline int testandset (int *spinlock) | |
410 | -{ | |
411 | - register unsigned int ret; | |
412 | - __asm__ __volatile__("swp %0, %1, [%2]" | |
413 | - : "=r"(ret) | |
414 | - : "0"(1), "r"(spinlock)); | |
415 | - | |
416 | - return ret; | |
417 | -} | |
418 | -#elif defined(__mc68000) | |
419 | -static inline int testandset (int *p) | |
420 | -{ | |
421 | - char ret; | |
422 | - __asm__ __volatile__("tas %1; sne %0" | |
423 | - : "=r" (ret) | |
424 | - : "m" (p) | |
425 | - : "cc","memory"); | |
426 | - return ret; | |
427 | -} | |
428 | -#elif defined(__hppa__) | |
429 | - | |
430 | -/* Because malloc only guarantees 8-byte alignment for malloc'd data, | |
431 | - and GCC only guarantees 8-byte alignment for stack locals, we can't | |
432 | - be assured of 16-byte alignment for atomic lock data even if we | |
433 | - specify "__attribute ((aligned(16)))" in the type declaration. So, | |
434 | - we use a struct containing an array of four ints for the atomic lock | |
435 | - type and dynamically select the 16-byte aligned int from the array | |
436 | - for the semaphore. */ | |
437 | -#define __PA_LDCW_ALIGNMENT 16 | |
438 | -static inline void *ldcw_align (void *p) { | |
439 | - unsigned long a = (unsigned long)p; | |
440 | - a = (a + __PA_LDCW_ALIGNMENT - 1) & ~(__PA_LDCW_ALIGNMENT - 1); | |
441 | - return (void *)a; | |
442 | -} | |
443 | - | |
444 | -static inline int testandset (spinlock_t *p) | |
445 | -{ | |
446 | - unsigned int ret; | |
447 | - p = ldcw_align(p); | |
448 | - __asm__ __volatile__("ldcw 0(%1),%0" | |
449 | - : "=r" (ret) | |
450 | - : "r" (p) | |
451 | - : "memory" ); | |
452 | - return !ret; | |
453 | -} | |
454 | - | |
455 | -#elif defined(__ia64) | |
456 | - | |
457 | -#include <ia64intrin.h> | |
458 | - | |
459 | -static inline int testandset (int *p) | |
460 | -{ | |
461 | - return __sync_lock_test_and_set (p, 1); | |
462 | -} | |
463 | -#elif defined(__mips__) | |
464 | -static inline int testandset (int *p) | |
465 | -{ | |
466 | - int ret; | |
467 | - | |
468 | - __asm__ __volatile__ ( | |
469 | - " .set push \n" | |
470 | - " .set noat \n" | |
471 | - " .set mips2 \n" | |
472 | - "1: li $1, 1 \n" | |
473 | - " ll %0, %1 \n" | |
474 | - " sc $1, %1 \n" | |
475 | - " beqz $1, 1b \n" | |
476 | - " .set pop " | |
477 | - : "=r" (ret), "+R" (*p) | |
478 | - : | |
479 | - : "memory"); | |
480 | - | |
481 | - return ret; | |
482 | -} | |
483 | -#else | |
484 | -#error unimplemented CPU support | |
485 | -#endif | |
486 | - | |
487 | -#if defined(CONFIG_USER_ONLY) | |
488 | -static inline void spin_lock(spinlock_t *lock) | |
489 | -{ | |
490 | - while (testandset(lock)); | |
491 | -} | |
492 | - | |
493 | -static inline void spin_unlock(spinlock_t *lock) | |
494 | -{ | |
495 | - resetlock(lock); | |
496 | -} | |
497 | - | |
498 | -static inline int spin_trylock(spinlock_t *lock) | |
499 | -{ | |
500 | - return !testandset(lock); | |
501 | -} | |
502 | -#else | |
503 | -static inline void spin_lock(spinlock_t *lock) | |
504 | -{ | |
505 | -} | |
506 | - | |
507 | -static inline void spin_unlock(spinlock_t *lock) | |
508 | -{ | |
509 | -} | |
510 | - | |
511 | -static inline int spin_trylock(spinlock_t *lock) | |
512 | -{ | |
513 | - return 1; | |
514 | -} | |
515 | -#endif | |
305 | +#include "qemu-lock.h" | |
516 | 306 | |
517 | 307 | extern spinlock_t tb_lock; |
518 | 308 | ... | ... |
exec.c
... | ... | @@ -1341,10 +1341,20 @@ void cpu_set_log_filename(const char *filename) |
1341 | 1341 | /* mask must never be zero, except for A20 change call */ |
1342 | 1342 | void cpu_interrupt(CPUState *env, int mask) |
1343 | 1343 | { |
1344 | +#if !defined(USE_NPTL) | |
1344 | 1345 | TranslationBlock *tb; |
1345 | 1346 | static spinlock_t interrupt_lock = SPIN_LOCK_UNLOCKED; |
1347 | +#endif | |
1346 | 1348 | |
1349 | + /* FIXME: This is probably not threadsafe. A different thread could | |
1350 | + be in the mittle of a read-modify-write operation. */ | |
1347 | 1351 | env->interrupt_request |= mask; |
1352 | +#if defined(USE_NPTL) | |
1353 | + /* FIXME: TB unchaining isn't SMP safe. For now just ignore the | |
1354 | + problem and hope the cpu will stop of its own accord. For userspace | |
1355 | + emulation this often isn't actually as bad as it sounds. Often | |
1356 | + signals are used primarily to interrupt blocking syscalls. */ | |
1357 | +#else | |
1348 | 1358 | /* if the cpu is currently executing code, we must unlink it and |
1349 | 1359 | all the potentially executing TB */ |
1350 | 1360 | tb = env->current_tb; |
... | ... | @@ -1353,6 +1363,7 @@ void cpu_interrupt(CPUState *env, int mask) |
1353 | 1363 | tb_reset_jump_recursive(tb); |
1354 | 1364 | resetlock(&interrupt_lock); |
1355 | 1365 | } |
1366 | +#endif | |
1356 | 1367 | } |
1357 | 1368 | |
1358 | 1369 | void cpu_reset_interrupt(CPUState *env, int mask) |
... | ... | @@ -2015,7 +2026,6 @@ void page_set_flags(target_ulong start, target_ulong end, int flags) |
2015 | 2026 | end = TARGET_PAGE_ALIGN(end); |
2016 | 2027 | if (flags & PAGE_WRITE) |
2017 | 2028 | flags |= PAGE_WRITE_ORG; |
2018 | - spin_lock(&tb_lock); | |
2019 | 2029 | for(addr = start; addr < end; addr += TARGET_PAGE_SIZE) { |
2020 | 2030 | p = page_find_alloc(addr >> TARGET_PAGE_BITS); |
2021 | 2031 | /* if the write protection is set, then we invalidate the code |
... | ... | @@ -2027,7 +2037,6 @@ void page_set_flags(target_ulong start, target_ulong end, int flags) |
2027 | 2037 | } |
2028 | 2038 | p->flags = flags; |
2029 | 2039 | } |
2030 | - spin_unlock(&tb_lock); | |
2031 | 2040 | } |
2032 | 2041 | |
2033 | 2042 | int page_check_range(target_ulong start, target_ulong len, int flags) | ... | ... |
linux-user/elfload.c
... | ... | @@ -89,7 +89,7 @@ enum { |
89 | 89 | static const char *get_elf_platform(void) |
90 | 90 | { |
91 | 91 | static char elf_platform[] = "i386"; |
92 | - int family = (global_env->cpuid_version >> 8) & 0xff; | |
92 | + int family = (thread_env->cpuid_version >> 8) & 0xff; | |
93 | 93 | if (family > 6) |
94 | 94 | family = 6; |
95 | 95 | if (family >= 3) |
... | ... | @@ -101,7 +101,7 @@ static const char *get_elf_platform(void) |
101 | 101 | |
102 | 102 | static uint32_t get_elf_hwcap(void) |
103 | 103 | { |
104 | - return global_env->cpuid_features; | |
104 | + return thread_env->cpuid_features; | |
105 | 105 | } |
106 | 106 | |
107 | 107 | #ifdef TARGET_X86_64 | ... | ... |
linux-user/main.c
... | ... | @@ -26,6 +26,8 @@ |
26 | 26 | |
27 | 27 | #include "qemu.h" |
28 | 28 | #include "qemu-common.h" |
29 | +/* For tb_lock */ | |
30 | +#include "exec-all.h" | |
29 | 31 | |
30 | 32 | #define DEBUG_LOGFILE "/tmp/qemu.log" |
31 | 33 | |
... | ... | @@ -123,6 +125,135 @@ int64_t cpu_get_real_ticks(void) |
123 | 125 | |
124 | 126 | #endif |
125 | 127 | |
128 | +#if defined(USE_NPTL) | |
129 | +/***********************************************************/ | |
130 | +/* Helper routines for implementing atomic operations. */ | |
131 | + | |
132 | +/* To implement exclusive operations we force all cpus to syncronise. | |
133 | + We don't require a full sync, only that no cpus are executing guest code. | |
134 | + The alternative is to map target atomic ops onto host equivalents, | |
135 | + which requires quite a lot of per host/target work. */ | |
136 | +static pthread_mutex_t exclusive_lock = PTHREAD_MUTEX_INITIALIZER; | |
137 | +static pthread_cond_t exclusive_cond = PTHREAD_COND_INITIALIZER; | |
138 | +static pthread_cond_t exclusive_resume = PTHREAD_COND_INITIALIZER; | |
139 | +static int pending_cpus; | |
140 | + | |
141 | +/* Make sure everything is in a consistent state for calling fork(). */ | |
142 | +void fork_start(void) | |
143 | +{ | |
144 | + mmap_fork_start(); | |
145 | + pthread_mutex_lock(&tb_lock); | |
146 | + pthread_mutex_lock(&exclusive_lock); | |
147 | +} | |
148 | + | |
149 | +void fork_end(int child) | |
150 | +{ | |
151 | + if (child) { | |
152 | + /* Child processes created by fork() only have a single thread. | |
153 | + Discard information about the parent threads. */ | |
154 | + first_cpu = thread_env; | |
155 | + thread_env->next_cpu = NULL; | |
156 | + pending_cpus = 0; | |
157 | + pthread_mutex_init(&exclusive_lock, NULL); | |
158 | + pthread_cond_init(&exclusive_cond, NULL); | |
159 | + pthread_cond_init(&exclusive_resume, NULL); | |
160 | + pthread_mutex_init(&tb_lock, NULL); | |
161 | + } else { | |
162 | + pthread_mutex_unlock(&exclusive_lock); | |
163 | + pthread_mutex_unlock(&tb_lock); | |
164 | + } | |
165 | + mmap_fork_end(child); | |
166 | +} | |
167 | + | |
168 | +/* Wait for pending exclusive operations to complete. The exclusive lock | |
169 | + must be held. */ | |
170 | +static inline void exclusive_idle(void) | |
171 | +{ | |
172 | + while (pending_cpus) { | |
173 | + pthread_cond_wait(&exclusive_resume, &exclusive_lock); | |
174 | + } | |
175 | +} | |
176 | + | |
177 | +/* Start an exclusive operation. | |
178 | + Must only be called from outside cpu_arm_exec. */ | |
179 | +static inline void start_exclusive(void) | |
180 | +{ | |
181 | + CPUState *other; | |
182 | + pthread_mutex_lock(&exclusive_lock); | |
183 | + exclusive_idle(); | |
184 | + | |
185 | + pending_cpus = 1; | |
186 | + /* Make all other cpus stop executing. */ | |
187 | + for (other = first_cpu; other; other = other->next_cpu) { | |
188 | + if (other->running) { | |
189 | + pending_cpus++; | |
190 | + cpu_interrupt(other, CPU_INTERRUPT_EXIT); | |
191 | + } | |
192 | + } | |
193 | + if (pending_cpus > 1) { | |
194 | + pthread_cond_wait(&exclusive_cond, &exclusive_lock); | |
195 | + } | |
196 | +} | |
197 | + | |
198 | +/* Finish an exclusive operation. */ | |
199 | +static inline void end_exclusive(void) | |
200 | +{ | |
201 | + pending_cpus = 0; | |
202 | + pthread_cond_broadcast(&exclusive_resume); | |
203 | + pthread_mutex_unlock(&exclusive_lock); | |
204 | +} | |
205 | + | |
206 | +/* Wait for exclusive ops to finish, and begin cpu execution. */ | |
207 | +static inline void cpu_exec_start(CPUState *env) | |
208 | +{ | |
209 | + pthread_mutex_lock(&exclusive_lock); | |
210 | + exclusive_idle(); | |
211 | + env->running = 1; | |
212 | + pthread_mutex_unlock(&exclusive_lock); | |
213 | +} | |
214 | + | |
215 | +/* Mark cpu as not executing, and release pending exclusive ops. */ | |
216 | +static inline void cpu_exec_end(CPUState *env) | |
217 | +{ | |
218 | + pthread_mutex_lock(&exclusive_lock); | |
219 | + env->running = 0; | |
220 | + if (pending_cpus > 1) { | |
221 | + pending_cpus--; | |
222 | + if (pending_cpus == 1) { | |
223 | + pthread_cond_signal(&exclusive_cond); | |
224 | + } | |
225 | + } | |
226 | + exclusive_idle(); | |
227 | + pthread_mutex_unlock(&exclusive_lock); | |
228 | +} | |
229 | +#else /* if !USE_NPTL */ | |
230 | +/* These are no-ops because we are not threadsafe. */ | |
231 | +static inline void cpu_exec_start(CPUState *env) | |
232 | +{ | |
233 | +} | |
234 | + | |
235 | +static inline void cpu_exec_end(CPUState *env) | |
236 | +{ | |
237 | +} | |
238 | + | |
239 | +static inline void start_exclusive(void) | |
240 | +{ | |
241 | +} | |
242 | + | |
243 | +static inline void end_exclusive(void) | |
244 | +{ | |
245 | +} | |
246 | + | |
247 | +void fork_start(void) | |
248 | +{ | |
249 | +} | |
250 | + | |
251 | +void fork_end(int child) | |
252 | +{ | |
253 | +} | |
254 | +#endif | |
255 | + | |
256 | + | |
126 | 257 | #ifdef TARGET_I386 |
127 | 258 | /***********************************************************/ |
128 | 259 | /* CPUX86 core interface */ |
... | ... | @@ -378,8 +509,11 @@ do_kernel_trap(CPUARMState *env) |
378 | 509 | /* ??? No-op. Will need to do better for SMP. */ |
379 | 510 | break; |
380 | 511 | case 0xffff0fc0: /* __kernel_cmpxchg */ |
381 | - /* ??? This is not really atomic. However we don't support | |
382 | - threads anyway, so it doesn't realy matter. */ | |
512 | + /* XXX: This only works between threads, not between processes. | |
513 | + It's probably possible to implement this with native host | |
514 | + operations. However things like ldrex/strex are much harder so | |
515 | + there's not much point trying. */ | |
516 | + start_exclusive(); | |
383 | 517 | cpsr = cpsr_read(env); |
384 | 518 | addr = env->regs[2]; |
385 | 519 | /* FIXME: This should SEGV if the access fails. */ |
... | ... | @@ -396,6 +530,7 @@ do_kernel_trap(CPUARMState *env) |
396 | 530 | cpsr &= ~CPSR_C; |
397 | 531 | } |
398 | 532 | cpsr_write(env, cpsr, CPSR_C); |
533 | + end_exclusive(); | |
399 | 534 | break; |
400 | 535 | case 0xffff0fe0: /* __kernel_get_tls */ |
401 | 536 | env->regs[0] = env->cp15.c13_tls2; |
... | ... | @@ -422,7 +557,9 @@ void cpu_loop(CPUARMState *env) |
422 | 557 | uint32_t addr; |
423 | 558 | |
424 | 559 | for(;;) { |
560 | + cpu_exec_start(env); | |
425 | 561 | trapnr = cpu_arm_exec(env); |
562 | + cpu_exec_end(env); | |
426 | 563 | switch(trapnr) { |
427 | 564 | case EXCP_UDEF: |
428 | 565 | { |
... | ... | @@ -2044,8 +2181,7 @@ void usage(void) |
2044 | 2181 | _exit(1); |
2045 | 2182 | } |
2046 | 2183 | |
2047 | -/* XXX: currently only used for async signals (see signal.c) */ | |
2048 | -CPUState *global_env; | |
2184 | +THREAD CPUState *thread_env; | |
2049 | 2185 | |
2050 | 2186 | void init_task_state(TaskState *ts) |
2051 | 2187 | { |
... | ... | @@ -2203,7 +2339,7 @@ int main(int argc, char **argv) |
2203 | 2339 | fprintf(stderr, "Unable to find CPU definition\n"); |
2204 | 2340 | exit(1); |
2205 | 2341 | } |
2206 | - global_env = env; | |
2342 | + thread_env = env; | |
2207 | 2343 | |
2208 | 2344 | if (getenv("QEMU_STRACE")) { |
2209 | 2345 | do_strace = 1; | ... | ... |
linux-user/mmap.c
... | ... | @@ -46,6 +46,22 @@ void mmap_unlock(void) |
46 | 46 | pthread_mutex_unlock(&mmap_mutex); |
47 | 47 | } |
48 | 48 | } |
49 | + | |
50 | +/* Grab lock to make sure things are in a consistent state after fork(). */ | |
51 | +void mmap_fork_start(void) | |
52 | +{ | |
53 | + if (mmap_lock_count) | |
54 | + abort(); | |
55 | + pthread_mutex_lock(&mmap_mutex); | |
56 | +} | |
57 | + | |
58 | +void mmap_fork_end(int child) | |
59 | +{ | |
60 | + if (child) | |
61 | + pthread_mutex_init(&mmap_mutex, NULL); | |
62 | + else | |
63 | + pthread_mutex_unlock(&mmap_mutex); | |
64 | +} | |
49 | 65 | #else |
50 | 66 | /* We aren't threadsafe to start with, so no need to worry about locking. */ |
51 | 67 | void mmap_lock(void) | ... | ... |
linux-user/qemu.h
... | ... | @@ -37,6 +37,12 @@ typedef target_long abi_long; |
37 | 37 | #include "target_signal.h" |
38 | 38 | #include "gdbstub.h" |
39 | 39 | |
40 | +#if defined(USE_NPTL) | |
41 | +#define THREAD __thread | |
42 | +#else | |
43 | +#define THREAD | |
44 | +#endif | |
45 | + | |
40 | 46 | /* This struct is used to hold certain information about the image. |
41 | 47 | * Basically, it replicates in user space what would be certain |
42 | 48 | * task_struct fields in the kernel |
... | ... | @@ -184,12 +190,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, |
184 | 190 | abi_long arg2, abi_long arg3, abi_long arg4, |
185 | 191 | abi_long arg5, abi_long arg6); |
186 | 192 | void gemu_log(const char *fmt, ...) __attribute__((format(printf,1,2))); |
187 | -extern CPUState *global_env; | |
193 | +extern THREAD CPUState *thread_env; | |
188 | 194 | void cpu_loop(CPUState *env); |
189 | 195 | void init_paths(const char *prefix); |
190 | 196 | const char *path(const char *pathname); |
191 | 197 | char *target_strerror(int err); |
192 | 198 | int get_osversion(void); |
199 | +void fork_start(void); | |
200 | +void fork_end(int child); | |
193 | 201 | |
194 | 202 | extern int loglevel; |
195 | 203 | extern FILE *logfile; |
... | ... | @@ -235,6 +243,10 @@ int target_msync(abi_ulong start, abi_ulong len, int flags); |
235 | 243 | extern unsigned long last_brk; |
236 | 244 | void mmap_lock(void); |
237 | 245 | void mmap_unlock(void); |
246 | +#if defined(USE_NPTL) | |
247 | +void mmap_fork_start(void); | |
248 | +void mmap_fork_end(int child); | |
249 | +#endif | |
238 | 250 | |
239 | 251 | /* user access */ |
240 | 252 | ... | ... |
linux-user/signal.c
... | ... | @@ -424,9 +424,9 @@ static void host_signal_handler(int host_signum, siginfo_t *info, |
424 | 424 | fprintf(stderr, "qemu: got signal %d\n", sig); |
425 | 425 | #endif |
426 | 426 | host_to_target_siginfo_noswap(&tinfo, info); |
427 | - if (queue_signal(global_env, sig, &tinfo) == 1) { | |
427 | + if (queue_signal(thread_env, sig, &tinfo) == 1) { | |
428 | 428 | /* interrupt the virtual CPU as soon as possible */ |
429 | - cpu_interrupt(global_env, CPU_INTERRUPT_EXIT); | |
429 | + cpu_interrupt(thread_env, CPU_INTERRUPT_EXIT); | |
430 | 430 | } |
431 | 431 | } |
432 | 432 | ... | ... |
qemu-lock.h
0 โ 100644
1 | +/* | |
2 | + * Copyright (c) 2003 Fabrice Bellard | |
3 | + * | |
4 | + * This library is free software; you can redistribute it and/or | |
5 | + * modify it under the terms of the GNU Lesser General Public | |
6 | + * License as published by the Free Software Foundation; either | |
7 | + * version 2 of the License, or (at your option) any later version. | |
8 | + * | |
9 | + * This library is distributed in the hope that it will be useful, | |
10 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
12 | + * Lesser General Public License for more details. | |
13 | + * | |
14 | + * You should have received a copy of the GNU Lesser General Public | |
15 | + * License along with this library; if not, write to the Free Software | |
16 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
17 | + */ | |
18 | + | |
19 | +/* Locking primitives. Most of this code should be redundant - | |
20 | + system emulation doesn't need/use locking, NPTL userspace uses | |
21 | + pthread mutexes, and non-NPTL userspace isn't threadsafe anyway. | |
22 | + In either case a spinlock is probably the wrong kind of lock. | |
23 | + Spinlocks are only good if you know annother CPU has the lock and is | |
24 | + likely to release it soon. In environments where you have more threads | |
25 | + than physical CPUs (the extreme case being a single CPU host) a spinlock | |
26 | + simply wastes CPU until the OS decides to preempt it. */ | |
27 | +#if defined(USE_NPTL) | |
28 | + | |
29 | +#include <pthread.h> | |
30 | +#define spin_lock pthread_mutex_lock | |
31 | +#define spin_unlock pthread_mutex_unlock | |
32 | +#define spinlock_t pthread_mutex_t | |
33 | +#define SPIN_LOCK_UNLOCKED PTHREAD_MUTEX_INITIALIZER | |
34 | + | |
35 | +#else | |
36 | + | |
37 | +#if defined(__hppa__) | |
38 | + | |
39 | +typedef int spinlock_t[4]; | |
40 | + | |
41 | +#define SPIN_LOCK_UNLOCKED { 1, 1, 1, 1 } | |
42 | + | |
43 | +static inline void resetlock (spinlock_t *p) | |
44 | +{ | |
45 | + (*p)[0] = (*p)[1] = (*p)[2] = (*p)[3] = 1; | |
46 | +} | |
47 | + | |
48 | +#else | |
49 | + | |
50 | +typedef int spinlock_t; | |
51 | + | |
52 | +#define SPIN_LOCK_UNLOCKED 0 | |
53 | + | |
54 | +static inline void resetlock (spinlock_t *p) | |
55 | +{ | |
56 | + *p = SPIN_LOCK_UNLOCKED; | |
57 | +} | |
58 | + | |
59 | +#endif | |
60 | + | |
61 | +#if defined(__powerpc__) | |
62 | +static inline int testandset (int *p) | |
63 | +{ | |
64 | + int ret; | |
65 | + __asm__ __volatile__ ( | |
66 | + "0: lwarx %0,0,%1\n" | |
67 | + " xor. %0,%3,%0\n" | |
68 | + " bne 1f\n" | |
69 | + " stwcx. %2,0,%1\n" | |
70 | + " bne- 0b\n" | |
71 | + "1: " | |
72 | + : "=&r" (ret) | |
73 | + : "r" (p), "r" (1), "r" (0) | |
74 | + : "cr0", "memory"); | |
75 | + return ret; | |
76 | +} | |
77 | +#elif defined(__i386__) | |
78 | +static inline int testandset (int *p) | |
79 | +{ | |
80 | + long int readval = 0; | |
81 | + | |
82 | + __asm__ __volatile__ ("lock; cmpxchgl %2, %0" | |
83 | + : "+m" (*p), "+a" (readval) | |
84 | + : "r" (1) | |
85 | + : "cc"); | |
86 | + return readval; | |
87 | +} | |
88 | +#elif defined(__x86_64__) | |
89 | +static inline int testandset (int *p) | |
90 | +{ | |
91 | + long int readval = 0; | |
92 | + | |
93 | + __asm__ __volatile__ ("lock; cmpxchgl %2, %0" | |
94 | + : "+m" (*p), "+a" (readval) | |
95 | + : "r" (1) | |
96 | + : "cc"); | |
97 | + return readval; | |
98 | +} | |
99 | +#elif defined(__s390__) | |
100 | +static inline int testandset (int *p) | |
101 | +{ | |
102 | + int ret; | |
103 | + | |
104 | + __asm__ __volatile__ ("0: cs %0,%1,0(%2)\n" | |
105 | + " jl 0b" | |
106 | + : "=&d" (ret) | |
107 | + : "r" (1), "a" (p), "0" (*p) | |
108 | + : "cc", "memory" ); | |
109 | + return ret; | |
110 | +} | |
111 | +#elif defined(__alpha__) | |
112 | +static inline int testandset (int *p) | |
113 | +{ | |
114 | + int ret; | |
115 | + unsigned long one; | |
116 | + | |
117 | + __asm__ __volatile__ ("0: mov 1,%2\n" | |
118 | + " ldl_l %0,%1\n" | |
119 | + " stl_c %2,%1\n" | |
120 | + " beq %2,1f\n" | |
121 | + ".subsection 2\n" | |
122 | + "1: br 0b\n" | |
123 | + ".previous" | |
124 | + : "=r" (ret), "=m" (*p), "=r" (one) | |
125 | + : "m" (*p)); | |
126 | + return ret; | |
127 | +} | |
128 | +#elif defined(__sparc__) | |
129 | +static inline int testandset (int *p) | |
130 | +{ | |
131 | + int ret; | |
132 | + | |
133 | + __asm__ __volatile__("ldstub [%1], %0" | |
134 | + : "=r" (ret) | |
135 | + : "r" (p) | |
136 | + : "memory"); | |
137 | + | |
138 | + return (ret ? 1 : 0); | |
139 | +} | |
140 | +#elif defined(__arm__) | |
141 | +static inline int testandset (int *spinlock) | |
142 | +{ | |
143 | + register unsigned int ret; | |
144 | + __asm__ __volatile__("swp %0, %1, [%2]" | |
145 | + : "=r"(ret) | |
146 | + : "0"(1), "r"(spinlock)); | |
147 | + | |
148 | + return ret; | |
149 | +} | |
150 | +#elif defined(__mc68000) | |
151 | +static inline int testandset (int *p) | |
152 | +{ | |
153 | + char ret; | |
154 | + __asm__ __volatile__("tas %1; sne %0" | |
155 | + : "=r" (ret) | |
156 | + : "m" (p) | |
157 | + : "cc","memory"); | |
158 | + return ret; | |
159 | +} | |
160 | +#elif defined(__hppa__) | |
161 | + | |
162 | +/* Because malloc only guarantees 8-byte alignment for malloc'd data, | |
163 | + and GCC only guarantees 8-byte alignment for stack locals, we can't | |
164 | + be assured of 16-byte alignment for atomic lock data even if we | |
165 | + specify "__attribute ((aligned(16)))" in the type declaration. So, | |
166 | + we use a struct containing an array of four ints for the atomic lock | |
167 | + type and dynamically select the 16-byte aligned int from the array | |
168 | + for the semaphore. */ | |
169 | +#define __PA_LDCW_ALIGNMENT 16 | |
170 | +static inline void *ldcw_align (void *p) { | |
171 | + unsigned long a = (unsigned long)p; | |
172 | + a = (a + __PA_LDCW_ALIGNMENT - 1) & ~(__PA_LDCW_ALIGNMENT - 1); | |
173 | + return (void *)a; | |
174 | +} | |
175 | + | |
176 | +static inline int testandset (spinlock_t *p) | |
177 | +{ | |
178 | + unsigned int ret; | |
179 | + p = ldcw_align(p); | |
180 | + __asm__ __volatile__("ldcw 0(%1),%0" | |
181 | + : "=r" (ret) | |
182 | + : "r" (p) | |
183 | + : "memory" ); | |
184 | + return !ret; | |
185 | +} | |
186 | + | |
187 | +#elif defined(__ia64) | |
188 | + | |
189 | +#include <ia64intrin.h> | |
190 | + | |
191 | +static inline int testandset (int *p) | |
192 | +{ | |
193 | + return __sync_lock_test_and_set (p, 1); | |
194 | +} | |
195 | +#elif defined(__mips__) | |
196 | +static inline int testandset (int *p) | |
197 | +{ | |
198 | + int ret; | |
199 | + | |
200 | + __asm__ __volatile__ ( | |
201 | + " .set push \n" | |
202 | + " .set noat \n" | |
203 | + " .set mips2 \n" | |
204 | + "1: li $1, 1 \n" | |
205 | + " ll %0, %1 \n" | |
206 | + " sc $1, %1 \n" | |
207 | + " beqz $1, 1b \n" | |
208 | + " .set pop " | |
209 | + : "=r" (ret), "+R" (*p) | |
210 | + : | |
211 | + : "memory"); | |
212 | + | |
213 | + return ret; | |
214 | +} | |
215 | +#else | |
216 | +#error unimplemented CPU support | |
217 | +#endif | |
218 | + | |
219 | +#if defined(CONFIG_USER_ONLY) | |
220 | +static inline void spin_lock(spinlock_t *lock) | |
221 | +{ | |
222 | + while (testandset(lock)); | |
223 | +} | |
224 | + | |
225 | +static inline void spin_unlock(spinlock_t *lock) | |
226 | +{ | |
227 | + resetlock(lock); | |
228 | +} | |
229 | + | |
230 | +static inline int spin_trylock(spinlock_t *lock) | |
231 | +{ | |
232 | + return !testandset(lock); | |
233 | +} | |
234 | +#else | |
235 | +static inline void spin_lock(spinlock_t *lock) | |
236 | +{ | |
237 | +} | |
238 | + | |
239 | +static inline void spin_unlock(spinlock_t *lock) | |
240 | +{ | |
241 | +} | |
242 | + | |
243 | +static inline int spin_trylock(spinlock_t *lock) | |
244 | +{ | |
245 | + return 1; | |
246 | +} | |
247 | +#endif | |
248 | + | |
249 | +#endif | ... | ... |