Refactor CPUState handling out of vl.c
Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
This commit is contained in:
		
							parent
							
								
									de06f8d193
								
							
						
					
					
						commit
						296af7c952
					
				| @ -161,7 +161,7 @@ endif #CONFIG_BSD_USER | ||||
| # System emulator target
 | ||||
| ifdef CONFIG_SOFTMMU | ||||
| 
 | ||||
| obj-y = vl.o monitor.o machine.o gdbstub.o | ||||
| obj-y = vl.o cpus.o monitor.o machine.o gdbstub.o | ||||
| obj-y += qemu-timer.o | ||||
| # virtio has to be here due to weird dependency between PCI and virtio-net.
 | ||||
| # need to fix this properly
 | ||||
|  | ||||
							
								
								
									
										773
									
								
								cpus.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										773
									
								
								cpus.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,773 @@ | ||||
| /*
 | ||||
|  * QEMU System Emulator | ||||
|  * | ||||
|  * Copyright (c) 2003-2008 Fabrice Bellard | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||||
|  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| /* Needed early for CONFIG_BSD etc. */ | ||||
| #include "config-host.h" | ||||
| 
 | ||||
| #include "monitor.h" | ||||
| #include "sysemu.h" | ||||
| #include "gdbstub.h" | ||||
| #include "dma.h" | ||||
| #include "kvm.h" | ||||
| 
 | ||||
| #include "cpus.h" | ||||
| 
 | ||||
| static CPUState *cur_cpu; | ||||
| static CPUState *next_cpu; | ||||
| 
 | ||||
| /***********************************************************/ | ||||
| void hw_error(const char *fmt, ...) | ||||
| { | ||||
|     va_list ap; | ||||
|     CPUState *env; | ||||
| 
 | ||||
|     va_start(ap, fmt); | ||||
|     fprintf(stderr, "qemu: hardware error: "); | ||||
|     vfprintf(stderr, fmt, ap); | ||||
|     fprintf(stderr, "\n"); | ||||
|     for(env = first_cpu; env != NULL; env = env->next_cpu) { | ||||
|         fprintf(stderr, "CPU #%d:\n", env->cpu_index); | ||||
| #ifdef TARGET_I386 | ||||
|         cpu_dump_state(env, stderr, fprintf, X86_DUMP_FPU); | ||||
| #else | ||||
|         cpu_dump_state(env, stderr, fprintf, 0); | ||||
| #endif | ||||
|     } | ||||
|     va_end(ap); | ||||
|     abort(); | ||||
| } | ||||
| 
 | ||||
| void cpu_synchronize_all_states(void) | ||||
| { | ||||
|     CPUState *cpu; | ||||
| 
 | ||||
|     for (cpu = first_cpu; cpu; cpu = cpu->next_cpu) { | ||||
|         cpu_synchronize_state(cpu); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void cpu_synchronize_all_post_reset(void) | ||||
| { | ||||
|     CPUState *cpu; | ||||
| 
 | ||||
|     for (cpu = first_cpu; cpu; cpu = cpu->next_cpu) { | ||||
|         cpu_synchronize_post_reset(cpu); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void cpu_synchronize_all_post_init(void) | ||||
| { | ||||
|     CPUState *cpu; | ||||
| 
 | ||||
|     for (cpu = first_cpu; cpu; cpu = cpu->next_cpu) { | ||||
|         cpu_synchronize_post_init(cpu); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void do_vm_stop(int reason) | ||||
| { | ||||
|     if (vm_running) { | ||||
|         cpu_disable_ticks(); | ||||
|         vm_running = 0; | ||||
|         pause_all_vcpus(); | ||||
|         vm_state_notify(0, reason); | ||||
|         monitor_protocol_event(QEVENT_STOP, NULL); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static int cpu_can_run(CPUState *env) | ||||
| { | ||||
|     if (env->stop) | ||||
|         return 0; | ||||
|     if (env->stopped) | ||||
|         return 0; | ||||
|     if (!vm_running) | ||||
|         return 0; | ||||
|     return 1; | ||||
| } | ||||
| 
 | ||||
| static int cpu_has_work(CPUState *env) | ||||
| { | ||||
|     if (env->stop) | ||||
|         return 1; | ||||
|     if (env->stopped) | ||||
|         return 0; | ||||
|     if (!env->halted) | ||||
|         return 1; | ||||
|     if (qemu_cpu_has_work(env)) | ||||
|         return 1; | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int tcg_has_work(void) | ||||
| { | ||||
|     CPUState *env; | ||||
| 
 | ||||
|     for (env = first_cpu; env != NULL; env = env->next_cpu) | ||||
|         if (cpu_has_work(env)) | ||||
|             return 1; | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| #ifndef _WIN32 | ||||
| static int io_thread_fd = -1; | ||||
| 
 | ||||
| static void qemu_event_increment(void) | ||||
| { | ||||
|     /* Write 8 bytes to be compatible with eventfd.  */ | ||||
|     static uint64_t val = 1; | ||||
|     ssize_t ret; | ||||
| 
 | ||||
|     if (io_thread_fd == -1) | ||||
|         return; | ||||
| 
 | ||||
|     do { | ||||
|         ret = write(io_thread_fd, &val, sizeof(val)); | ||||
|     } while (ret < 0 && errno == EINTR); | ||||
| 
 | ||||
|     /* EAGAIN is fine, a read must be pending.  */ | ||||
|     if (ret < 0 && errno != EAGAIN) { | ||||
|         fprintf(stderr, "qemu_event_increment: write() filed: %s\n", | ||||
|                 strerror(errno)); | ||||
|         exit (1); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void qemu_event_read(void *opaque) | ||||
| { | ||||
|     int fd = (unsigned long)opaque; | ||||
|     ssize_t len; | ||||
|     char buffer[512]; | ||||
| 
 | ||||
|     /* Drain the notify pipe.  For eventfd, only 8 bytes will be read.  */ | ||||
|     do { | ||||
|         len = read(fd, buffer, sizeof(buffer)); | ||||
|     } while ((len == -1 && errno == EINTR) || len == sizeof(buffer)); | ||||
| } | ||||
| 
 | ||||
| static int qemu_event_init(void) | ||||
| { | ||||
|     int err; | ||||
|     int fds[2]; | ||||
| 
 | ||||
|     err = qemu_eventfd(fds); | ||||
|     if (err == -1) | ||||
|         return -errno; | ||||
| 
 | ||||
|     err = fcntl_setfl(fds[0], O_NONBLOCK); | ||||
|     if (err < 0) | ||||
|         goto fail; | ||||
| 
 | ||||
|     err = fcntl_setfl(fds[1], O_NONBLOCK); | ||||
|     if (err < 0) | ||||
|         goto fail; | ||||
| 
 | ||||
|     qemu_set_fd_handler2(fds[0], NULL, qemu_event_read, NULL, | ||||
|                          (void *)(unsigned long)fds[0]); | ||||
| 
 | ||||
|     io_thread_fd = fds[1]; | ||||
|     return 0; | ||||
| 
 | ||||
| fail: | ||||
|     close(fds[0]); | ||||
|     close(fds[1]); | ||||
|     return err; | ||||
| } | ||||
| #else | ||||
| HANDLE qemu_event_handle; | ||||
| 
 | ||||
| static void dummy_event_handler(void *opaque) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static int qemu_event_init(void) | ||||
| { | ||||
|     qemu_event_handle = CreateEvent(NULL, FALSE, FALSE, NULL); | ||||
|     if (!qemu_event_handle) { | ||||
|         fprintf(stderr, "Failed CreateEvent: %ld\n", GetLastError()); | ||||
|         return -1; | ||||
|     } | ||||
|     qemu_add_wait_object(qemu_event_handle, dummy_event_handler, NULL); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static void qemu_event_increment(void) | ||||
| { | ||||
|     if (!SetEvent(qemu_event_handle)) { | ||||
|         fprintf(stderr, "qemu_event_increment: SetEvent failed: %ld\n", | ||||
|                 GetLastError()); | ||||
|         exit (1); | ||||
|     } | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #ifndef CONFIG_IOTHREAD | ||||
| int qemu_init_main_loop(void) | ||||
| { | ||||
|     return qemu_event_init(); | ||||
| } | ||||
| 
 | ||||
| void qemu_init_vcpu(void *_env) | ||||
| { | ||||
|     CPUState *env = _env; | ||||
| 
 | ||||
|     env->nr_cores = smp_cores; | ||||
|     env->nr_threads = smp_threads; | ||||
|     if (kvm_enabled()) | ||||
|         kvm_init_vcpu(env); | ||||
|     return; | ||||
| } | ||||
| 
 | ||||
| int qemu_cpu_self(void *env) | ||||
| { | ||||
|     return 1; | ||||
| } | ||||
| 
 | ||||
| void resume_all_vcpus(void) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void pause_all_vcpus(void) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void qemu_cpu_kick(void *env) | ||||
| { | ||||
|     return; | ||||
| } | ||||
| 
 | ||||
| void qemu_notify_event(void) | ||||
| { | ||||
|     CPUState *env = cpu_single_env; | ||||
| 
 | ||||
|     qemu_event_increment (); | ||||
|     if (env) { | ||||
|         cpu_exit(env); | ||||
|     } | ||||
|     if (next_cpu && env != next_cpu) { | ||||
|         cpu_exit(next_cpu); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void qemu_mutex_lock_iothread(void) {} | ||||
| void qemu_mutex_unlock_iothread(void) {} | ||||
| 
 | ||||
| void vm_stop(int reason) | ||||
| { | ||||
|     do_vm_stop(reason); | ||||
| } | ||||
| 
 | ||||
| #else /* CONFIG_IOTHREAD */ | ||||
| 
 | ||||
| #include "qemu-thread.h" | ||||
| 
 | ||||
| QemuMutex qemu_global_mutex; | ||||
| static QemuMutex qemu_fair_mutex; | ||||
| 
 | ||||
| static QemuThread io_thread; | ||||
| 
 | ||||
| static QemuThread *tcg_cpu_thread; | ||||
| static QemuCond *tcg_halt_cond; | ||||
| 
 | ||||
| static int qemu_system_ready; | ||||
| /* cpu creation */ | ||||
| static QemuCond qemu_cpu_cond; | ||||
| /* system init */ | ||||
| static QemuCond qemu_system_cond; | ||||
| static QemuCond qemu_pause_cond; | ||||
| 
 | ||||
| static void tcg_block_io_signals(void); | ||||
| static void kvm_block_io_signals(CPUState *env); | ||||
| static void unblock_io_signals(void); | ||||
| 
 | ||||
| int qemu_init_main_loop(void) | ||||
| { | ||||
|     int ret; | ||||
| 
 | ||||
|     ret = qemu_event_init(); | ||||
|     if (ret) | ||||
|         return ret; | ||||
| 
 | ||||
|     qemu_cond_init(&qemu_pause_cond); | ||||
|     qemu_mutex_init(&qemu_fair_mutex); | ||||
|     qemu_mutex_init(&qemu_global_mutex); | ||||
|     qemu_mutex_lock(&qemu_global_mutex); | ||||
| 
 | ||||
|     unblock_io_signals(); | ||||
|     qemu_thread_self(&io_thread); | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static void qemu_wait_io_event_common(CPUState *env) | ||||
| { | ||||
|     if (env->stop) { | ||||
|         env->stop = 0; | ||||
|         env->stopped = 1; | ||||
|         qemu_cond_signal(&qemu_pause_cond); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void qemu_wait_io_event(CPUState *env) | ||||
| { | ||||
|     while (!tcg_has_work()) | ||||
|         qemu_cond_timedwait(env->halt_cond, &qemu_global_mutex, 1000); | ||||
| 
 | ||||
|     qemu_mutex_unlock(&qemu_global_mutex); | ||||
| 
 | ||||
|     /*
 | ||||
|      * Users of qemu_global_mutex can be starved, having no chance | ||||
|      * to acquire it since this path will get to it first. | ||||
|      * So use another lock to provide fairness. | ||||
|      */ | ||||
|     qemu_mutex_lock(&qemu_fair_mutex); | ||||
|     qemu_mutex_unlock(&qemu_fair_mutex); | ||||
| 
 | ||||
|     qemu_mutex_lock(&qemu_global_mutex); | ||||
|     qemu_wait_io_event_common(env); | ||||
| } | ||||
| 
 | ||||
| static void qemu_kvm_eat_signal(CPUState *env, int timeout) | ||||
| { | ||||
|     struct timespec ts; | ||||
|     int r, e; | ||||
|     siginfo_t siginfo; | ||||
|     sigset_t waitset; | ||||
| 
 | ||||
|     ts.tv_sec = timeout / 1000; | ||||
|     ts.tv_nsec = (timeout % 1000) * 1000000; | ||||
| 
 | ||||
|     sigemptyset(&waitset); | ||||
|     sigaddset(&waitset, SIG_IPI); | ||||
| 
 | ||||
|     qemu_mutex_unlock(&qemu_global_mutex); | ||||
|     r = sigtimedwait(&waitset, &siginfo, &ts); | ||||
|     e = errno; | ||||
|     qemu_mutex_lock(&qemu_global_mutex); | ||||
| 
 | ||||
|     if (r == -1 && !(e == EAGAIN || e == EINTR)) { | ||||
|         fprintf(stderr, "sigtimedwait: %s\n", strerror(e)); | ||||
|         exit(1); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void qemu_kvm_wait_io_event(CPUState *env) | ||||
| { | ||||
|     while (!cpu_has_work(env)) | ||||
|         qemu_cond_timedwait(env->halt_cond, &qemu_global_mutex, 1000); | ||||
| 
 | ||||
|     qemu_kvm_eat_signal(env, 0); | ||||
|     qemu_wait_io_event_common(env); | ||||
| } | ||||
| 
 | ||||
| static int qemu_cpu_exec(CPUState *env); | ||||
| 
 | ||||
| static void *kvm_cpu_thread_fn(void *arg) | ||||
| { | ||||
|     CPUState *env = arg; | ||||
| 
 | ||||
|     qemu_thread_self(env->thread); | ||||
|     if (kvm_enabled()) | ||||
|         kvm_init_vcpu(env); | ||||
| 
 | ||||
|     kvm_block_io_signals(env); | ||||
| 
 | ||||
|     /* signal CPU creation */ | ||||
|     qemu_mutex_lock(&qemu_global_mutex); | ||||
|     env->created = 1; | ||||
|     qemu_cond_signal(&qemu_cpu_cond); | ||||
| 
 | ||||
|     /* and wait for machine initialization */ | ||||
|     while (!qemu_system_ready) | ||||
|         qemu_cond_timedwait(&qemu_system_cond, &qemu_global_mutex, 100); | ||||
| 
 | ||||
|     while (1) { | ||||
|         if (cpu_can_run(env)) | ||||
|             qemu_cpu_exec(env); | ||||
|         qemu_kvm_wait_io_event(env); | ||||
|     } | ||||
| 
 | ||||
|     return NULL; | ||||
| } | ||||
| 
 | ||||
| static void *tcg_cpu_thread_fn(void *arg) | ||||
| { | ||||
|     CPUState *env = arg; | ||||
| 
 | ||||
|     tcg_block_io_signals(); | ||||
|     qemu_thread_self(env->thread); | ||||
| 
 | ||||
|     /* signal CPU creation */ | ||||
|     qemu_mutex_lock(&qemu_global_mutex); | ||||
|     for (env = first_cpu; env != NULL; env = env->next_cpu) | ||||
|         env->created = 1; | ||||
|     qemu_cond_signal(&qemu_cpu_cond); | ||||
| 
 | ||||
|     /* and wait for machine initialization */ | ||||
|     while (!qemu_system_ready) | ||||
|         qemu_cond_timedwait(&qemu_system_cond, &qemu_global_mutex, 100); | ||||
| 
 | ||||
|     while (1) { | ||||
|         tcg_cpu_exec(); | ||||
|         qemu_wait_io_event(cur_cpu); | ||||
|     } | ||||
| 
 | ||||
|     return NULL; | ||||
| } | ||||
| 
 | ||||
| void qemu_cpu_kick(void *_env) | ||||
| { | ||||
|     CPUState *env = _env; | ||||
|     qemu_cond_broadcast(env->halt_cond); | ||||
|     if (kvm_enabled()) | ||||
|         qemu_thread_signal(env->thread, SIG_IPI); | ||||
| } | ||||
| 
 | ||||
| int qemu_cpu_self(void *_env) | ||||
| { | ||||
|     CPUState *env = _env; | ||||
|     QemuThread this; | ||||
| 
 | ||||
|     qemu_thread_self(&this); | ||||
| 
 | ||||
|     return qemu_thread_equal(&this, env->thread); | ||||
| } | ||||
| 
 | ||||
| static void cpu_signal(int sig) | ||||
| { | ||||
|     if (cpu_single_env) | ||||
|         cpu_exit(cpu_single_env); | ||||
| } | ||||
| 
 | ||||
| static void tcg_block_io_signals(void) | ||||
| { | ||||
|     sigset_t set; | ||||
|     struct sigaction sigact; | ||||
| 
 | ||||
|     sigemptyset(&set); | ||||
|     sigaddset(&set, SIGUSR2); | ||||
|     sigaddset(&set, SIGIO); | ||||
|     sigaddset(&set, SIGALRM); | ||||
|     sigaddset(&set, SIGCHLD); | ||||
|     pthread_sigmask(SIG_BLOCK, &set, NULL); | ||||
| 
 | ||||
|     sigemptyset(&set); | ||||
|     sigaddset(&set, SIG_IPI); | ||||
|     pthread_sigmask(SIG_UNBLOCK, &set, NULL); | ||||
| 
 | ||||
|     memset(&sigact, 0, sizeof(sigact)); | ||||
|     sigact.sa_handler = cpu_signal; | ||||
|     sigaction(SIG_IPI, &sigact, NULL); | ||||
| } | ||||
| 
 | ||||
| static void dummy_signal(int sig) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static void kvm_block_io_signals(CPUState *env) | ||||
| { | ||||
|     int r; | ||||
|     sigset_t set; | ||||
|     struct sigaction sigact; | ||||
| 
 | ||||
|     sigemptyset(&set); | ||||
|     sigaddset(&set, SIGUSR2); | ||||
|     sigaddset(&set, SIGIO); | ||||
|     sigaddset(&set, SIGALRM); | ||||
|     sigaddset(&set, SIGCHLD); | ||||
|     sigaddset(&set, SIG_IPI); | ||||
|     pthread_sigmask(SIG_BLOCK, &set, NULL); | ||||
| 
 | ||||
|     pthread_sigmask(SIG_BLOCK, NULL, &set); | ||||
|     sigdelset(&set, SIG_IPI); | ||||
| 
 | ||||
|     memset(&sigact, 0, sizeof(sigact)); | ||||
|     sigact.sa_handler = dummy_signal; | ||||
|     sigaction(SIG_IPI, &sigact, NULL); | ||||
| 
 | ||||
|     r = kvm_set_signal_mask(env, &set); | ||||
|     if (r) { | ||||
|         fprintf(stderr, "kvm_set_signal_mask: %s\n", strerror(r)); | ||||
|         exit(1); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void unblock_io_signals(void) | ||||
| { | ||||
|     sigset_t set; | ||||
| 
 | ||||
|     sigemptyset(&set); | ||||
|     sigaddset(&set, SIGUSR2); | ||||
|     sigaddset(&set, SIGIO); | ||||
|     sigaddset(&set, SIGALRM); | ||||
|     pthread_sigmask(SIG_UNBLOCK, &set, NULL); | ||||
| 
 | ||||
|     sigemptyset(&set); | ||||
|     sigaddset(&set, SIG_IPI); | ||||
|     pthread_sigmask(SIG_BLOCK, &set, NULL); | ||||
| } | ||||
| 
 | ||||
| static void qemu_signal_lock(unsigned int msecs) | ||||
| { | ||||
|     qemu_mutex_lock(&qemu_fair_mutex); | ||||
| 
 | ||||
|     while (qemu_mutex_trylock(&qemu_global_mutex)) { | ||||
|         qemu_thread_signal(tcg_cpu_thread, SIG_IPI); | ||||
|         if (!qemu_mutex_timedlock(&qemu_global_mutex, msecs)) | ||||
|             break; | ||||
|     } | ||||
|     qemu_mutex_unlock(&qemu_fair_mutex); | ||||
| } | ||||
| 
 | ||||
| void qemu_mutex_lock_iothread(void) | ||||
| { | ||||
|     if (kvm_enabled()) { | ||||
|         qemu_mutex_lock(&qemu_fair_mutex); | ||||
|         qemu_mutex_lock(&qemu_global_mutex); | ||||
|         qemu_mutex_unlock(&qemu_fair_mutex); | ||||
|     } else | ||||
|         qemu_signal_lock(100); | ||||
| } | ||||
| 
 | ||||
| void qemu_mutex_unlock_iothread(void) | ||||
| { | ||||
|     qemu_mutex_unlock(&qemu_global_mutex); | ||||
| } | ||||
| 
 | ||||
| static int all_vcpus_paused(void) | ||||
| { | ||||
|     CPUState *penv = first_cpu; | ||||
| 
 | ||||
|     while (penv) { | ||||
|         if (!penv->stopped) | ||||
|             return 0; | ||||
|         penv = (CPUState *)penv->next_cpu; | ||||
|     } | ||||
| 
 | ||||
|     return 1; | ||||
| } | ||||
| 
 | ||||
| void pause_all_vcpus(void) | ||||
| { | ||||
|     CPUState *penv = first_cpu; | ||||
| 
 | ||||
|     while (penv) { | ||||
|         penv->stop = 1; | ||||
|         qemu_thread_signal(penv->thread, SIG_IPI); | ||||
|         qemu_cpu_kick(penv); | ||||
|         penv = (CPUState *)penv->next_cpu; | ||||
|     } | ||||
| 
 | ||||
|     while (!all_vcpus_paused()) { | ||||
|         qemu_cond_timedwait(&qemu_pause_cond, &qemu_global_mutex, 100); | ||||
|         penv = first_cpu; | ||||
|         while (penv) { | ||||
|             qemu_thread_signal(penv->thread, SIG_IPI); | ||||
|             penv = (CPUState *)penv->next_cpu; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void resume_all_vcpus(void) | ||||
| { | ||||
|     CPUState *penv = first_cpu; | ||||
| 
 | ||||
|     while (penv) { | ||||
|         penv->stop = 0; | ||||
|         penv->stopped = 0; | ||||
|         qemu_thread_signal(penv->thread, SIG_IPI); | ||||
|         qemu_cpu_kick(penv); | ||||
|         penv = (CPUState *)penv->next_cpu; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void tcg_init_vcpu(void *_env) | ||||
| { | ||||
|     CPUState *env = _env; | ||||
|     /* share a single thread for all cpus with TCG */ | ||||
|     if (!tcg_cpu_thread) { | ||||
|         env->thread = qemu_mallocz(sizeof(QemuThread)); | ||||
|         env->halt_cond = qemu_mallocz(sizeof(QemuCond)); | ||||
|         qemu_cond_init(env->halt_cond); | ||||
|         qemu_thread_create(env->thread, tcg_cpu_thread_fn, env); | ||||
|         while (env->created == 0) | ||||
|             qemu_cond_timedwait(&qemu_cpu_cond, &qemu_global_mutex, 100); | ||||
|         tcg_cpu_thread = env->thread; | ||||
|         tcg_halt_cond = env->halt_cond; | ||||
|     } else { | ||||
|         env->thread = tcg_cpu_thread; | ||||
|         env->halt_cond = tcg_halt_cond; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void kvm_start_vcpu(CPUState *env) | ||||
| { | ||||
|     env->thread = qemu_mallocz(sizeof(QemuThread)); | ||||
|     env->halt_cond = qemu_mallocz(sizeof(QemuCond)); | ||||
|     qemu_cond_init(env->halt_cond); | ||||
|     qemu_thread_create(env->thread, kvm_cpu_thread_fn, env); | ||||
|     while (env->created == 0) | ||||
|         qemu_cond_timedwait(&qemu_cpu_cond, &qemu_global_mutex, 100); | ||||
| } | ||||
| 
 | ||||
| void qemu_init_vcpu(void *_env) | ||||
| { | ||||
|     CPUState *env = _env; | ||||
| 
 | ||||
|     env->nr_cores = smp_cores; | ||||
|     env->nr_threads = smp_threads; | ||||
|     if (kvm_enabled()) | ||||
|         kvm_start_vcpu(env); | ||||
|     else | ||||
|         tcg_init_vcpu(env); | ||||
| } | ||||
| 
 | ||||
| void qemu_notify_event(void) | ||||
| { | ||||
|     qemu_event_increment(); | ||||
| } | ||||
| 
 | ||||
| static void qemu_system_vmstop_request(int reason) | ||||
| { | ||||
|     vmstop_requested = reason; | ||||
|     qemu_notify_event(); | ||||
| } | ||||
| 
 | ||||
| void vm_stop(int reason) | ||||
| { | ||||
|     QemuThread me; | ||||
|     qemu_thread_self(&me); | ||||
| 
 | ||||
|     if (!qemu_thread_equal(&me, &io_thread)) { | ||||
|         qemu_system_vmstop_request(reason); | ||||
|         /*
 | ||||
|          * FIXME: should not return to device code in case | ||||
|          * vm_stop() has been requested. | ||||
|          */ | ||||
|         if (cpu_single_env) { | ||||
|             cpu_exit(cpu_single_env); | ||||
|             cpu_single_env->stop = 1; | ||||
|         } | ||||
|         return; | ||||
|     } | ||||
|     do_vm_stop(reason); | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| static int qemu_cpu_exec(CPUState *env) | ||||
| { | ||||
|     int ret; | ||||
| #ifdef CONFIG_PROFILER | ||||
|     int64_t ti; | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_PROFILER | ||||
|     ti = profile_getclock(); | ||||
| #endif | ||||
|     if (use_icount) { | ||||
|         int64_t count; | ||||
|         int decr; | ||||
|         qemu_icount -= (env->icount_decr.u16.low + env->icount_extra); | ||||
|         env->icount_decr.u16.low = 0; | ||||
|         env->icount_extra = 0; | ||||
|         count = qemu_icount_round (qemu_next_deadline()); | ||||
|         qemu_icount += count; | ||||
|         decr = (count > 0xffff) ? 0xffff : count; | ||||
|         count -= decr; | ||||
|         env->icount_decr.u16.low = decr; | ||||
|         env->icount_extra = count; | ||||
|     } | ||||
|     ret = cpu_exec(env); | ||||
| #ifdef CONFIG_PROFILER | ||||
|     qemu_time += profile_getclock() - ti; | ||||
| #endif | ||||
|     if (use_icount) { | ||||
|         /* Fold pending instructions back into the
 | ||||
|            instruction counter, and clear the interrupt flag.  */ | ||||
|         qemu_icount -= (env->icount_decr.u16.low | ||||
|                         + env->icount_extra); | ||||
|         env->icount_decr.u32 = 0; | ||||
|         env->icount_extra = 0; | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| bool tcg_cpu_exec(void) | ||||
| { | ||||
|     int ret = 0; | ||||
| 
 | ||||
|     if (next_cpu == NULL) | ||||
|         next_cpu = first_cpu; | ||||
|     for (; next_cpu != NULL; next_cpu = next_cpu->next_cpu) { | ||||
|         CPUState *env = cur_cpu = next_cpu; | ||||
| 
 | ||||
|         qemu_clock_enable(vm_clock, | ||||
|                           (cur_cpu->singlestep_enabled & SSTEP_NOTIMER) == 0); | ||||
| 
 | ||||
|         if (qemu_alarm_pending()) | ||||
|             break; | ||||
|         if (cpu_can_run(env)) | ||||
|             ret = qemu_cpu_exec(env); | ||||
|         else if (env->stop) | ||||
|             break; | ||||
| 
 | ||||
|         if (ret == EXCP_DEBUG) { | ||||
|             gdb_set_stop_cpu(env); | ||||
|             debug_requested = EXCP_DEBUG; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     return tcg_has_work(); | ||||
| } | ||||
| 
 | ||||
| void set_numa_modes(void) | ||||
| { | ||||
|     CPUState *env; | ||||
|     int i; | ||||
| 
 | ||||
|     for (env = first_cpu; env != NULL; env = env->next_cpu) { | ||||
|         for (i = 0; i < nb_numa_nodes; i++) { | ||||
|             if (node_cpumask[i] & (1 << env->cpu_index)) { | ||||
|                 env->numa_node = i; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void set_cpu_log(const char *optarg) | ||||
| { | ||||
|     int mask; | ||||
|     const CPULogItem *item; | ||||
| 
 | ||||
|     mask = cpu_str_to_log_mask(optarg); | ||||
|     if (!mask) { | ||||
|         printf("Log items (comma separated):\n"); | ||||
|         for (item = cpu_log_items; item->mask != 0; item++) { | ||||
|             printf("%-10s %s\n", item->name, item->help); | ||||
|         } | ||||
|         exit(1); | ||||
|     } | ||||
|     cpu_set_log(mask); | ||||
| } | ||||
							
								
								
									
										18
									
								
								cpus.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								cpus.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| #ifndef QEMU_CPUS_H | ||||
| #define QEMU_CPUS_H | ||||
| 
 | ||||
| /* cpu-common.c */ | ||||
| int qemu_init_main_loop(void); | ||||
| void resume_all_vcpus(void); | ||||
| void pause_all_vcpus(void); | ||||
| 
 | ||||
| /* vl.c */ | ||||
| extern int smp_cores; | ||||
| extern int smp_threads; | ||||
| extern int debug_requested; | ||||
| void vm_state_notify(int running, int reason); | ||||
| bool tcg_cpu_exec(void); | ||||
| void set_numa_modes(void); | ||||
| void set_cpu_log(const char *optarg); | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										749
									
								
								vl.c
									
									
									
									
									
								
							
							
						
						
									
										749
									
								
								vl.c
									
									
									
									
									
								
							| @ -160,6 +160,7 @@ int main(int argc, char **argv) | ||||
| #include "slirp/libslirp.h" | ||||
| 
 | ||||
| #include "qemu-queue.h" | ||||
| #include "cpus.h" | ||||
| 
 | ||||
| //#define DEBUG_NET
 | ||||
| //#define DEBUG_SLIRP
 | ||||
| @ -249,8 +250,6 @@ int nb_numa_nodes; | ||||
| uint64_t node_mem[MAX_NODES]; | ||||
| uint64_t node_cpumask[MAX_NODES]; | ||||
| 
 | ||||
| static CPUState *cur_cpu; | ||||
| static CPUState *next_cpu; | ||||
| static QEMUTimer *nographic_timer; | ||||
| 
 | ||||
| uint8_t qemu_uuid[16]; | ||||
| @ -314,28 +313,6 @@ static int default_driver_check(QemuOpts *opts, void *opaque) | ||||
| target_phys_addr_t isa_mem_base = 0; | ||||
| PicState2 *isa_pic; | ||||
| 
 | ||||
| /***********************************************************/ | ||||
| void hw_error(const char *fmt, ...) | ||||
| { | ||||
|     va_list ap; | ||||
|     CPUState *env; | ||||
| 
 | ||||
|     va_start(ap, fmt); | ||||
|     fprintf(stderr, "qemu: hardware error: "); | ||||
|     vfprintf(stderr, fmt, ap); | ||||
|     fprintf(stderr, "\n"); | ||||
|     for(env = first_cpu; env != NULL; env = env->next_cpu) { | ||||
|         fprintf(stderr, "CPU #%d:\n", env->cpu_index); | ||||
| #ifdef TARGET_I386 | ||||
|         cpu_dump_state(env, stderr, fprintf, X86_DUMP_FPU); | ||||
| #else | ||||
|         cpu_dump_state(env, stderr, fprintf, 0); | ||||
| #endif | ||||
|     } | ||||
|     va_end(ap); | ||||
|     abort(); | ||||
| } | ||||
| 
 | ||||
| static void set_proc_name(const char *s) | ||||
| { | ||||
| #if defined(__linux__) && defined(PR_SET_NAME) | ||||
| @ -1962,33 +1939,6 @@ static void nographic_update(void *opaque) | ||||
|     qemu_mod_timer(nographic_timer, interval + qemu_get_clock(rt_clock)); | ||||
| } | ||||
| 
 | ||||
| void cpu_synchronize_all_states(void) | ||||
| { | ||||
|     CPUState *cpu; | ||||
| 
 | ||||
|     for (cpu = first_cpu; cpu; cpu = cpu->next_cpu) { | ||||
|         cpu_synchronize_state(cpu); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void cpu_synchronize_all_post_reset(void) | ||||
| { | ||||
|     CPUState *cpu; | ||||
| 
 | ||||
|     for (cpu = first_cpu; cpu; cpu = cpu->next_cpu) { | ||||
|         cpu_synchronize_post_reset(cpu); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void cpu_synchronize_all_post_init(void) | ||||
| { | ||||
|     CPUState *cpu; | ||||
| 
 | ||||
|     for (cpu = first_cpu; cpu; cpu = cpu->next_cpu) { | ||||
|         cpu_synchronize_post_init(cpu); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| struct vm_change_state_entry { | ||||
|     VMChangeStateHandler *cb; | ||||
|     void *opaque; | ||||
| @ -2016,7 +1966,7 @@ void qemu_del_vm_change_state_handler(VMChangeStateEntry *e) | ||||
|     qemu_free (e); | ||||
| } | ||||
| 
 | ||||
| static void vm_state_notify(int running, int reason) | ||||
| void vm_state_notify(int running, int reason) | ||||
| { | ||||
|     VMChangeStateEntry *e; | ||||
| 
 | ||||
| @ -2025,9 +1975,6 @@ static void vm_state_notify(int running, int reason) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void resume_all_vcpus(void); | ||||
| static void pause_all_vcpus(void); | ||||
| 
 | ||||
| void vm_start(void) | ||||
| { | ||||
|     if (!vm_running) { | ||||
| @ -2051,7 +1998,7 @@ static QTAILQ_HEAD(reset_handlers, QEMUResetEntry) reset_handlers = | ||||
| static int reset_requested; | ||||
| static int shutdown_requested; | ||||
| static int powerdown_requested; | ||||
| static int debug_requested; | ||||
| int debug_requested; | ||||
| static int vmstop_requested; | ||||
| 
 | ||||
| int qemu_shutdown_requested(void) | ||||
| @ -2089,17 +2036,6 @@ static int qemu_vmstop_requested(void) | ||||
|     return r; | ||||
| } | ||||
| 
 | ||||
| static void do_vm_stop(int reason) | ||||
| { | ||||
|     if (vm_running) { | ||||
|         cpu_disable_ticks(); | ||||
|         vm_running = 0; | ||||
|         pause_all_vcpus(); | ||||
|         vm_state_notify(0, reason); | ||||
|         monitor_protocol_event(QEVENT_STOP, NULL); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void qemu_register_reset(QEMUResetHandler *func, void *opaque) | ||||
| { | ||||
|     QEMUResetEntry *re = qemu_mallocz(sizeof(QEMUResetEntry)); | ||||
| @ -2156,589 +2092,6 @@ void qemu_system_powerdown_request(void) | ||||
|     qemu_notify_event(); | ||||
| } | ||||
| 
 | ||||
| static int cpu_can_run(CPUState *env) | ||||
| { | ||||
|     if (env->stop) | ||||
|         return 0; | ||||
|     if (env->stopped) | ||||
|         return 0; | ||||
|     if (!vm_running) | ||||
|         return 0; | ||||
|     return 1; | ||||
| } | ||||
| 
 | ||||
| static int cpu_has_work(CPUState *env) | ||||
| { | ||||
|     if (env->stop) | ||||
|         return 1; | ||||
|     if (env->stopped) | ||||
|         return 0; | ||||
|     if (!env->halted) | ||||
|         return 1; | ||||
|     if (qemu_cpu_has_work(env)) | ||||
|         return 1; | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int tcg_has_work(void) | ||||
| { | ||||
|     CPUState *env; | ||||
| 
 | ||||
|     for (env = first_cpu; env != NULL; env = env->next_cpu) | ||||
|         if (cpu_has_work(env)) | ||||
|             return 1; | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| #ifndef _WIN32 | ||||
| static int io_thread_fd = -1; | ||||
| 
 | ||||
| static void qemu_event_increment(void) | ||||
| { | ||||
|     /* Write 8 bytes to be compatible with eventfd.  */ | ||||
|     static uint64_t val = 1; | ||||
|     ssize_t ret; | ||||
| 
 | ||||
|     if (io_thread_fd == -1) | ||||
|         return; | ||||
| 
 | ||||
|     do { | ||||
|         ret = write(io_thread_fd, &val, sizeof(val)); | ||||
|     } while (ret < 0 && errno == EINTR); | ||||
| 
 | ||||
|     /* EAGAIN is fine, a read must be pending.  */ | ||||
|     if (ret < 0 && errno != EAGAIN) { | ||||
|         fprintf(stderr, "qemu_event_increment: write() filed: %s\n", | ||||
|                 strerror(errno)); | ||||
|         exit (1); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void qemu_event_read(void *opaque) | ||||
| { | ||||
|     int fd = (unsigned long)opaque; | ||||
|     ssize_t len; | ||||
|     char buffer[512]; | ||||
| 
 | ||||
|     /* Drain the notify pipe.  For eventfd, only 8 bytes will be read.  */ | ||||
|     do { | ||||
|         len = read(fd, buffer, sizeof(buffer)); | ||||
|     } while ((len == -1 && errno == EINTR) || len == sizeof(buffer)); | ||||
| } | ||||
| 
 | ||||
| static int qemu_event_init(void) | ||||
| { | ||||
|     int err; | ||||
|     int fds[2]; | ||||
| 
 | ||||
|     err = qemu_eventfd(fds); | ||||
|     if (err == -1) | ||||
|         return -errno; | ||||
| 
 | ||||
|     err = fcntl_setfl(fds[0], O_NONBLOCK); | ||||
|     if (err < 0) | ||||
|         goto fail; | ||||
| 
 | ||||
|     err = fcntl_setfl(fds[1], O_NONBLOCK); | ||||
|     if (err < 0) | ||||
|         goto fail; | ||||
| 
 | ||||
|     qemu_set_fd_handler2(fds[0], NULL, qemu_event_read, NULL, | ||||
|                          (void *)(unsigned long)fds[0]); | ||||
| 
 | ||||
|     io_thread_fd = fds[1]; | ||||
|     return 0; | ||||
| 
 | ||||
| fail: | ||||
|     close(fds[0]); | ||||
|     close(fds[1]); | ||||
|     return err; | ||||
| } | ||||
| #else | ||||
| HANDLE qemu_event_handle; | ||||
| 
 | ||||
| static void dummy_event_handler(void *opaque) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static int qemu_event_init(void) | ||||
| { | ||||
|     qemu_event_handle = CreateEvent(NULL, FALSE, FALSE, NULL); | ||||
|     if (!qemu_event_handle) { | ||||
|         fprintf(stderr, "Failed CreateEvent: %ld\n", GetLastError()); | ||||
|         return -1; | ||||
|     } | ||||
|     qemu_add_wait_object(qemu_event_handle, dummy_event_handler, NULL); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static void qemu_event_increment(void) | ||||
| { | ||||
|     if (!SetEvent(qemu_event_handle)) { | ||||
|         fprintf(stderr, "qemu_event_increment: SetEvent failed: %ld\n", | ||||
|                 GetLastError()); | ||||
|         exit (1); | ||||
|     } | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #ifndef CONFIG_IOTHREAD | ||||
| static int qemu_init_main_loop(void) | ||||
| { | ||||
|     return qemu_event_init(); | ||||
| } | ||||
| 
 | ||||
| void qemu_init_vcpu(void *_env) | ||||
| { | ||||
|     CPUState *env = _env; | ||||
| 
 | ||||
|     env->nr_cores = smp_cores; | ||||
|     env->nr_threads = smp_threads; | ||||
|     if (kvm_enabled()) | ||||
|         kvm_init_vcpu(env); | ||||
|     return; | ||||
| } | ||||
| 
 | ||||
| int qemu_cpu_self(void *env) | ||||
| { | ||||
|     return 1; | ||||
| } | ||||
| 
 | ||||
| static void resume_all_vcpus(void) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static void pause_all_vcpus(void) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void qemu_cpu_kick(void *env) | ||||
| { | ||||
|     return; | ||||
| } | ||||
| 
 | ||||
| void qemu_notify_event(void) | ||||
| { | ||||
|     CPUState *env = cpu_single_env; | ||||
| 
 | ||||
|     qemu_event_increment (); | ||||
|     if (env) { | ||||
|         cpu_exit(env); | ||||
|     } | ||||
|     if (next_cpu && env != next_cpu) { | ||||
| 	cpu_exit(next_cpu); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void qemu_mutex_lock_iothread(void) {} | ||||
| void qemu_mutex_unlock_iothread(void) {} | ||||
| 
 | ||||
| void vm_stop(int reason) | ||||
| { | ||||
|     do_vm_stop(reason); | ||||
| } | ||||
| 
 | ||||
| #else /* CONFIG_IOTHREAD */ | ||||
| 
 | ||||
| #include "qemu-thread.h" | ||||
| 
 | ||||
| QemuMutex qemu_global_mutex; | ||||
| static QemuMutex qemu_fair_mutex; | ||||
| 
 | ||||
| static QemuThread io_thread; | ||||
| 
 | ||||
| static QemuThread *tcg_cpu_thread; | ||||
| static QemuCond *tcg_halt_cond; | ||||
| 
 | ||||
| static int qemu_system_ready; | ||||
| /* cpu creation */ | ||||
| static QemuCond qemu_cpu_cond; | ||||
| /* system init */ | ||||
| static QemuCond qemu_system_cond; | ||||
| static QemuCond qemu_pause_cond; | ||||
| 
 | ||||
| static void tcg_block_io_signals(void); | ||||
| static void kvm_block_io_signals(CPUState *env); | ||||
| static void unblock_io_signals(void); | ||||
| 
 | ||||
| static int qemu_init_main_loop(void) | ||||
| { | ||||
|     int ret; | ||||
| 
 | ||||
|     ret = qemu_event_init(); | ||||
|     if (ret) | ||||
|         return ret; | ||||
| 
 | ||||
|     qemu_cond_init(&qemu_pause_cond); | ||||
|     qemu_mutex_init(&qemu_fair_mutex); | ||||
|     qemu_mutex_init(&qemu_global_mutex); | ||||
|     qemu_mutex_lock(&qemu_global_mutex); | ||||
| 
 | ||||
|     unblock_io_signals(); | ||||
|     qemu_thread_self(&io_thread); | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static void qemu_wait_io_event_common(CPUState *env) | ||||
| { | ||||
|     if (env->stop) { | ||||
|         env->stop = 0; | ||||
|         env->stopped = 1; | ||||
|         qemu_cond_signal(&qemu_pause_cond); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void qemu_wait_io_event(CPUState *env) | ||||
| { | ||||
|     while (!tcg_has_work()) | ||||
|         qemu_cond_timedwait(env->halt_cond, &qemu_global_mutex, 1000); | ||||
| 
 | ||||
|     qemu_mutex_unlock(&qemu_global_mutex); | ||||
| 
 | ||||
|     /*
 | ||||
|      * Users of qemu_global_mutex can be starved, having no chance | ||||
|      * to acquire it since this path will get to it first. | ||||
|      * So use another lock to provide fairness. | ||||
|      */ | ||||
|     qemu_mutex_lock(&qemu_fair_mutex); | ||||
|     qemu_mutex_unlock(&qemu_fair_mutex); | ||||
| 
 | ||||
|     qemu_mutex_lock(&qemu_global_mutex); | ||||
|     qemu_wait_io_event_common(env); | ||||
| } | ||||
| 
 | ||||
| static void qemu_kvm_eat_signal(CPUState *env, int timeout) | ||||
| { | ||||
|     struct timespec ts; | ||||
|     int r, e; | ||||
|     siginfo_t siginfo; | ||||
|     sigset_t waitset; | ||||
| 
 | ||||
|     ts.tv_sec = timeout / 1000; | ||||
|     ts.tv_nsec = (timeout % 1000) * 1000000; | ||||
| 
 | ||||
|     sigemptyset(&waitset); | ||||
|     sigaddset(&waitset, SIG_IPI); | ||||
| 
 | ||||
|     qemu_mutex_unlock(&qemu_global_mutex); | ||||
|     r = sigtimedwait(&waitset, &siginfo, &ts); | ||||
|     e = errno; | ||||
|     qemu_mutex_lock(&qemu_global_mutex); | ||||
| 
 | ||||
|     if (r == -1 && !(e == EAGAIN || e == EINTR)) { | ||||
|         fprintf(stderr, "sigtimedwait: %s\n", strerror(e)); | ||||
|         exit(1); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void qemu_kvm_wait_io_event(CPUState *env) | ||||
| { | ||||
|     while (!cpu_has_work(env)) | ||||
|         qemu_cond_timedwait(env->halt_cond, &qemu_global_mutex, 1000); | ||||
| 
 | ||||
|     qemu_kvm_eat_signal(env, 0); | ||||
|     qemu_wait_io_event_common(env); | ||||
| } | ||||
| 
 | ||||
| static int qemu_cpu_exec(CPUState *env); | ||||
| 
 | ||||
| static void *kvm_cpu_thread_fn(void *arg) | ||||
| { | ||||
|     CPUState *env = arg; | ||||
| 
 | ||||
|     qemu_thread_self(env->thread); | ||||
|     if (kvm_enabled()) | ||||
|         kvm_init_vcpu(env); | ||||
| 
 | ||||
|     kvm_block_io_signals(env); | ||||
| 
 | ||||
|     /* signal CPU creation */ | ||||
|     qemu_mutex_lock(&qemu_global_mutex); | ||||
|     env->created = 1; | ||||
|     qemu_cond_signal(&qemu_cpu_cond); | ||||
| 
 | ||||
|     /* and wait for machine initialization */ | ||||
|     while (!qemu_system_ready) | ||||
|         qemu_cond_timedwait(&qemu_system_cond, &qemu_global_mutex, 100); | ||||
| 
 | ||||
|     while (1) { | ||||
|         if (cpu_can_run(env)) | ||||
|             qemu_cpu_exec(env); | ||||
|         qemu_kvm_wait_io_event(env); | ||||
|     } | ||||
| 
 | ||||
|     return NULL; | ||||
| } | ||||
| 
 | ||||
| static bool tcg_cpu_exec(void); | ||||
| 
 | ||||
| static void *tcg_cpu_thread_fn(void *arg) | ||||
| { | ||||
|     CPUState *env = arg; | ||||
| 
 | ||||
|     tcg_block_io_signals(); | ||||
|     qemu_thread_self(env->thread); | ||||
| 
 | ||||
|     /* signal CPU creation */ | ||||
|     qemu_mutex_lock(&qemu_global_mutex); | ||||
|     for (env = first_cpu; env != NULL; env = env->next_cpu) | ||||
|         env->created = 1; | ||||
|     qemu_cond_signal(&qemu_cpu_cond); | ||||
| 
 | ||||
|     /* and wait for machine initialization */ | ||||
|     while (!qemu_system_ready) | ||||
|         qemu_cond_timedwait(&qemu_system_cond, &qemu_global_mutex, 100); | ||||
| 
 | ||||
|     while (1) { | ||||
|         tcg_cpu_exec(); | ||||
|         qemu_wait_io_event(cur_cpu); | ||||
|     } | ||||
| 
 | ||||
|     return NULL; | ||||
| } | ||||
| 
 | ||||
| void qemu_cpu_kick(void *_env) | ||||
| { | ||||
|     CPUState *env = _env; | ||||
|     qemu_cond_broadcast(env->halt_cond); | ||||
|     if (kvm_enabled()) | ||||
|         qemu_thread_signal(env->thread, SIG_IPI); | ||||
| } | ||||
| 
 | ||||
| int qemu_cpu_self(void *_env) | ||||
| { | ||||
|     CPUState *env = _env; | ||||
|     QemuThread this; | ||||
|   | ||||
|     qemu_thread_self(&this); | ||||
|   | ||||
|     return qemu_thread_equal(&this, env->thread); | ||||
| } | ||||
| 
 | ||||
| static void cpu_signal(int sig) | ||||
| { | ||||
|     if (cpu_single_env) | ||||
|         cpu_exit(cpu_single_env); | ||||
| } | ||||
| 
 | ||||
| static void tcg_block_io_signals(void) | ||||
| { | ||||
|     sigset_t set; | ||||
|     struct sigaction sigact; | ||||
| 
 | ||||
|     sigemptyset(&set); | ||||
|     sigaddset(&set, SIGUSR2); | ||||
|     sigaddset(&set, SIGIO); | ||||
|     sigaddset(&set, SIGALRM); | ||||
|     sigaddset(&set, SIGCHLD); | ||||
|     pthread_sigmask(SIG_BLOCK, &set, NULL); | ||||
| 
 | ||||
|     sigemptyset(&set); | ||||
|     sigaddset(&set, SIG_IPI); | ||||
|     pthread_sigmask(SIG_UNBLOCK, &set, NULL); | ||||
| 
 | ||||
|     memset(&sigact, 0, sizeof(sigact)); | ||||
|     sigact.sa_handler = cpu_signal; | ||||
|     sigaction(SIG_IPI, &sigact, NULL); | ||||
| } | ||||
| 
 | ||||
| static void dummy_signal(int sig) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static void kvm_block_io_signals(CPUState *env) | ||||
| { | ||||
|     int r; | ||||
|     sigset_t set; | ||||
|     struct sigaction sigact; | ||||
| 
 | ||||
|     sigemptyset(&set); | ||||
|     sigaddset(&set, SIGUSR2); | ||||
|     sigaddset(&set, SIGIO); | ||||
|     sigaddset(&set, SIGALRM); | ||||
|     sigaddset(&set, SIGCHLD); | ||||
|     sigaddset(&set, SIG_IPI); | ||||
|     pthread_sigmask(SIG_BLOCK, &set, NULL); | ||||
| 
 | ||||
|     pthread_sigmask(SIG_BLOCK, NULL, &set); | ||||
|     sigdelset(&set, SIG_IPI); | ||||
| 
 | ||||
|     memset(&sigact, 0, sizeof(sigact)); | ||||
|     sigact.sa_handler = dummy_signal; | ||||
|     sigaction(SIG_IPI, &sigact, NULL); | ||||
| 
 | ||||
|     r = kvm_set_signal_mask(env, &set); | ||||
|     if (r) { | ||||
|         fprintf(stderr, "kvm_set_signal_mask: %s\n", strerror(r)); | ||||
|         exit(1); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void unblock_io_signals(void) | ||||
| { | ||||
|     sigset_t set; | ||||
| 
 | ||||
|     sigemptyset(&set); | ||||
|     sigaddset(&set, SIGUSR2); | ||||
|     sigaddset(&set, SIGIO); | ||||
|     sigaddset(&set, SIGALRM); | ||||
|     pthread_sigmask(SIG_UNBLOCK, &set, NULL); | ||||
| 
 | ||||
|     sigemptyset(&set); | ||||
|     sigaddset(&set, SIG_IPI); | ||||
|     pthread_sigmask(SIG_BLOCK, &set, NULL); | ||||
| } | ||||
| 
 | ||||
| static void qemu_signal_lock(unsigned int msecs) | ||||
| { | ||||
|     qemu_mutex_lock(&qemu_fair_mutex); | ||||
| 
 | ||||
|     while (qemu_mutex_trylock(&qemu_global_mutex)) { | ||||
|         qemu_thread_signal(tcg_cpu_thread, SIG_IPI); | ||||
|         if (!qemu_mutex_timedlock(&qemu_global_mutex, msecs)) | ||||
|             break; | ||||
|     } | ||||
|     qemu_mutex_unlock(&qemu_fair_mutex); | ||||
| } | ||||
| 
 | ||||
| void qemu_mutex_lock_iothread(void) | ||||
| { | ||||
|     if (kvm_enabled()) { | ||||
|         qemu_mutex_lock(&qemu_fair_mutex); | ||||
|         qemu_mutex_lock(&qemu_global_mutex); | ||||
|         qemu_mutex_unlock(&qemu_fair_mutex); | ||||
|     } else | ||||
|         qemu_signal_lock(100); | ||||
| } | ||||
| 
 | ||||
| void qemu_mutex_unlock_iothread(void) | ||||
| { | ||||
|     qemu_mutex_unlock(&qemu_global_mutex); | ||||
| } | ||||
| 
 | ||||
| static int all_vcpus_paused(void) | ||||
| { | ||||
|     CPUState *penv = first_cpu; | ||||
| 
 | ||||
|     while (penv) { | ||||
|         if (!penv->stopped) | ||||
|             return 0; | ||||
|         penv = (CPUState *)penv->next_cpu; | ||||
|     } | ||||
| 
 | ||||
|     return 1; | ||||
| } | ||||
| 
 | ||||
| static void pause_all_vcpus(void) | ||||
| { | ||||
|     CPUState *penv = first_cpu; | ||||
| 
 | ||||
|     while (penv) { | ||||
|         penv->stop = 1; | ||||
|         qemu_thread_signal(penv->thread, SIG_IPI); | ||||
|         qemu_cpu_kick(penv); | ||||
|         penv = (CPUState *)penv->next_cpu; | ||||
|     } | ||||
| 
 | ||||
|     while (!all_vcpus_paused()) { | ||||
|         qemu_cond_timedwait(&qemu_pause_cond, &qemu_global_mutex, 100); | ||||
|         penv = first_cpu; | ||||
|         while (penv) { | ||||
|             qemu_thread_signal(penv->thread, SIG_IPI); | ||||
|             penv = (CPUState *)penv->next_cpu; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void resume_all_vcpus(void) | ||||
| { | ||||
|     CPUState *penv = first_cpu; | ||||
| 
 | ||||
|     while (penv) { | ||||
|         penv->stop = 0; | ||||
|         penv->stopped = 0; | ||||
|         qemu_thread_signal(penv->thread, SIG_IPI); | ||||
|         qemu_cpu_kick(penv); | ||||
|         penv = (CPUState *)penv->next_cpu; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void tcg_init_vcpu(void *_env) | ||||
| { | ||||
|     CPUState *env = _env; | ||||
|     /* share a single thread for all cpus with TCG */ | ||||
|     if (!tcg_cpu_thread) { | ||||
|         env->thread = qemu_mallocz(sizeof(QemuThread)); | ||||
|         env->halt_cond = qemu_mallocz(sizeof(QemuCond)); | ||||
|         qemu_cond_init(env->halt_cond); | ||||
|         qemu_thread_create(env->thread, tcg_cpu_thread_fn, env); | ||||
|         while (env->created == 0) | ||||
|             qemu_cond_timedwait(&qemu_cpu_cond, &qemu_global_mutex, 100); | ||||
|         tcg_cpu_thread = env->thread; | ||||
|         tcg_halt_cond = env->halt_cond; | ||||
|     } else { | ||||
|         env->thread = tcg_cpu_thread; | ||||
|         env->halt_cond = tcg_halt_cond; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void kvm_start_vcpu(CPUState *env) | ||||
| { | ||||
|     env->thread = qemu_mallocz(sizeof(QemuThread)); | ||||
|     env->halt_cond = qemu_mallocz(sizeof(QemuCond)); | ||||
|     qemu_cond_init(env->halt_cond); | ||||
|     qemu_thread_create(env->thread, kvm_cpu_thread_fn, env); | ||||
|     while (env->created == 0) | ||||
|         qemu_cond_timedwait(&qemu_cpu_cond, &qemu_global_mutex, 100); | ||||
| } | ||||
| 
 | ||||
| void qemu_init_vcpu(void *_env) | ||||
| { | ||||
|     CPUState *env = _env; | ||||
| 
 | ||||
|     env->nr_cores = smp_cores; | ||||
|     env->nr_threads = smp_threads; | ||||
|     if (kvm_enabled()) | ||||
|         kvm_start_vcpu(env); | ||||
|     else | ||||
|         tcg_init_vcpu(env); | ||||
| } | ||||
| 
 | ||||
| void qemu_notify_event(void) | ||||
| { | ||||
|     qemu_event_increment(); | ||||
| } | ||||
| 
 | ||||
| static void qemu_system_vmstop_request(int reason) | ||||
| { | ||||
|     vmstop_requested = reason; | ||||
|     qemu_notify_event(); | ||||
| } | ||||
| 
 | ||||
| void vm_stop(int reason) | ||||
| { | ||||
|     QemuThread me; | ||||
|     qemu_thread_self(&me); | ||||
| 
 | ||||
|     if (!qemu_thread_equal(&me, &io_thread)) { | ||||
|         qemu_system_vmstop_request(reason); | ||||
|         /*
 | ||||
|          * FIXME: should not return to device code in case | ||||
|          * vm_stop() has been requested. | ||||
|          */ | ||||
|         if (cpu_single_env) { | ||||
|             cpu_exit(cpu_single_env); | ||||
|             cpu_single_env->stop = 1; | ||||
|         } | ||||
|         return; | ||||
|     } | ||||
|     do_vm_stop(reason); | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| #ifdef _WIN32 | ||||
| static void host_main_loop_wait(int *timeout) | ||||
| { | ||||
| @ -2865,102 +2218,6 @@ void main_loop_wait(int nonblocking) | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| static int qemu_cpu_exec(CPUState *env) | ||||
| { | ||||
|     int ret; | ||||
| #ifdef CONFIG_PROFILER | ||||
|     int64_t ti; | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_PROFILER | ||||
|     ti = profile_getclock(); | ||||
| #endif | ||||
|     if (use_icount) { | ||||
|         int64_t count; | ||||
|         int decr; | ||||
|         qemu_icount -= (env->icount_decr.u16.low + env->icount_extra); | ||||
|         env->icount_decr.u16.low = 0; | ||||
|         env->icount_extra = 0; | ||||
|         count = qemu_icount_round (qemu_next_deadline()); | ||||
|         qemu_icount += count; | ||||
|         decr = (count > 0xffff) ? 0xffff : count; | ||||
|         count -= decr; | ||||
|         env->icount_decr.u16.low = decr; | ||||
|         env->icount_extra = count; | ||||
|     } | ||||
|     ret = cpu_exec(env); | ||||
| #ifdef CONFIG_PROFILER | ||||
|     qemu_time += profile_getclock() - ti; | ||||
| #endif | ||||
|     if (use_icount) { | ||||
|         /* Fold pending instructions back into the
 | ||||
|            instruction counter, and clear the interrupt flag.  */ | ||||
|         qemu_icount -= (env->icount_decr.u16.low | ||||
|                         + env->icount_extra); | ||||
|         env->icount_decr.u32 = 0; | ||||
|         env->icount_extra = 0; | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| static bool tcg_cpu_exec(void) | ||||
| { | ||||
|     int ret = 0; | ||||
| 
 | ||||
|     if (next_cpu == NULL) | ||||
|         next_cpu = first_cpu; | ||||
|     for (; next_cpu != NULL; next_cpu = next_cpu->next_cpu) { | ||||
|         CPUState *env = cur_cpu = next_cpu; | ||||
| 
 | ||||
|         qemu_clock_enable(vm_clock, | ||||
|                           (cur_cpu->singlestep_enabled & SSTEP_NOTIMER) == 0); | ||||
| 
 | ||||
|         if (qemu_alarm_pending()) | ||||
|             break; | ||||
|         if (cpu_can_run(env)) | ||||
|             ret = qemu_cpu_exec(env); | ||||
|         else if (env->stop) | ||||
|             break; | ||||
| 
 | ||||
|         if (ret == EXCP_DEBUG) { | ||||
|             gdb_set_stop_cpu(env); | ||||
|             debug_requested = EXCP_DEBUG; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     return tcg_has_work(); | ||||
| } | ||||
| 
 | ||||
| static void set_numa_modes(void) | ||||
| { | ||||
|     CPUState *env; | ||||
|     int i; | ||||
| 
 | ||||
|     for (env = first_cpu; env != NULL; env = env->next_cpu) { | ||||
|         for (i = 0; i < nb_numa_nodes; i++) { | ||||
|             if (node_cpumask[i] & (1 << env->cpu_index)) { | ||||
|                 env->numa_node = i; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void set_cpu_log(const char *optarg) | ||||
| { | ||||
|     int mask; | ||||
|     const CPULogItem *item; | ||||
| 
 | ||||
|     mask = cpu_str_to_log_mask(optarg); | ||||
|     if (!mask) { | ||||
|         printf("Log items (comma separated):\n"); | ||||
|         for (item = cpu_log_items; item->mask != 0; item++) { | ||||
|             printf("%-10s %s\n", item->name, item->help); | ||||
|         } | ||||
|         exit(1); | ||||
|     } | ||||
|     cpu_set_log(mask); | ||||
| } | ||||
| 
 | ||||
| static int vm_can_run(void) | ||||
| { | ||||
|     if (powerdown_requested) | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Blue Swirl
						Blue Swirl