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 | ... | ... |