[PATCH 4/5] QEMU Release vcpu and finally exit vcpu thread safely

From: Liu Ping Fan
Date: Sat Nov 26 2011 - 21:46:26 EST


From: Liu Ping Fan <pingfank@xxxxxxxxxxxxxxxxxx>

When guest driver tell us that the vcpu is no longer needed,
qemu can release the vcpu and finally exit vcpu thread

Signed-off-by: Liu Ping Fan <pingfank@xxxxxxxxxxxxxxxxxx>
---
cpu-defs.h | 5 +++++
cpus.c | 21 +++++++++++++++++++++
hmp-commands.hx | 2 +-
hw/acpi_piix4.c | 19 ++++++++++++++++---
hw/pci_cpustate.c | 22 ++++++++++++++++++++++
kvm-all.c | 11 ++++++++++-
monitor.c | 12 +++++++-----
7 files changed, 82 insertions(+), 10 deletions(-)

diff --git a/cpu-defs.h b/cpu-defs.h
index db48a7a..cb69a07 100644
--- a/cpu-defs.h
+++ b/cpu-defs.h
@@ -153,6 +153,10 @@ typedef struct CPUWatchpoint {
QTAILQ_ENTRY(CPUWatchpoint) entry;
} CPUWatchpoint;

+#define CPU_STATE_RUNNING 0
+#define CPU_STATE_ZAPREQ 1
+#define CPU_STATE_ZAPPED 2
+
#define CPU_TEMP_BUF_NLONGS 128
#define CPU_COMMON \
struct TranslationBlock *current_tb; /* currently executing TB */ \
@@ -210,6 +214,7 @@ typedef struct CPUWatchpoint {
uint32_t created; \
uint32_t stop; /* Stop request */ \
uint32_t stopped; /* Artificially stopped */ \
+ uint32_t state; /*state indicator*/ \
struct QemuThread *thread; \
struct QemuCond *halt_cond; \
int thread_kicked; \
diff --git a/cpus.c b/cpus.c
index c996ac5..e479476 100644
--- a/cpus.c
+++ b/cpus.c
@@ -33,6 +33,7 @@

#include "qemu-thread.h"
#include "cpus.h"
+#include "cpu.h"

#ifndef _WIN32
#include "compatfd.h"
@@ -778,6 +779,7 @@ static void qemu_kvm_wait_io_event(CPUState *env)
static void *qemu_kvm_cpu_thread_fn(void *arg)
{
CPUState *env = arg;
+ CPUState *prev = NULL;
int r;

qemu_mutex_lock(&qemu_global_mutex);
@@ -808,10 +810,29 @@ static void *qemu_kvm_cpu_thread_fn(void *arg)
cpu_handle_guest_debug(env);
}
}
+ /*1,try to zap; 2, can safe to destroy*/
+ if (env->state == CPU_STATE_ZAPPED) {
+ goto zapout;
+ }
qemu_kvm_wait_io_event(env);
}

return NULL;
+zapout:
+ prev = first_cpu;
+ if (prev == env) {
+ first_cpu = env->next_cpu;
+ } else {
+ while (prev != NULL) {
+ if (prev->next_cpu == env) {
+ break;
+ }
+ prev = prev->next_cpu;
+ }
+ prev->next_cpu = env->next_cpu;
+ }
+ cpu_free(env);
+ return NULL;
}

static void *qemu_tcg_cpu_thread_fn(void *arg)
diff --git a/hmp-commands.hx b/hmp-commands.hx
index ed5c9b9..b642a34 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1218,7 +1218,7 @@ ETEXI
{
.name = "cpu_set",
.args_type = "cpu:i,state:s",
- .params = "cpu [online|offline]",
+ .params = "cpu [online|offline|zap]",
.help = "change cpu state",
.mhandler.cmd = do_cpu_set_nr,
},
diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c
index f585226..1f3ed06 100644
--- a/hw/acpi_piix4.c
+++ b/hw/acpi_piix4.c
@@ -605,10 +605,23 @@ void qemu_system_cpu_hot_add(int cpu, int state)
env->cpuid_apic_id = cpu;
}

- if (state)
- enable_processor(s, cpu);
- else
+ switch (state) {
+ /*zap vcpu*/
+ case 0:
+ env = qemu_get_cpu(cpu);
+ /*1 means try to zap*/
+ env->state = CPU_STATE_ZAPREQ;
+ disable_processor(s, cpu);
+ break;
+ /*offline vcpu*/
+ case 1:
disable_processor(s, cpu);
+ break;
+ /*onine vcpu*/
+ case 2:
+ enable_processor(s, cpu);
+ break;
+ }

pm_update_sci(s);
}
diff --git a/hw/pci_cpustate.c b/hw/pci_cpustate.c
index fd31a1f..18402cf 100644
--- a/hw/pci_cpustate.c
+++ b/hw/pci_cpustate.c
@@ -24,6 +24,8 @@
#include "loader.h"
#include "sysemu.h"
#include "iov.h"
+#include <linux/kvm.h>
+#include "kvm.h"

#define PCI_DEVICE_ID_CPUSTATE 0x1010
#define CPUSTATE_REGS_SIZE 0x1000
@@ -52,6 +54,26 @@ static void
cpustate_mmio_write(void *opaque, target_phys_addr_t addr, uint64_t val,
unsigned size)
{
+ CPUState *env;
+ int ret;
+ struct kvm_vcpu_state state;
+ switch (addr) {
+ /*apic id*/
+ case 0:
+ env = cpu_phyid_to_cpu(val);
+ if (env != NULL) {
+ if (env->state == CPU_STATE_ZAPREQ) {
+ state.vcpu_id = env->cpu_index;
+ state.state = 1;
+ ret = kvm_vm_ioctl(env->kvm_state, KVM_SETSTATE_VCPU, &state);
+ }
+ }
+ break;
+ case 4:
+ break;
+ default:
+ break;
+ }
}

static uint64_t
diff --git a/kvm-all.c b/kvm-all.c
index 8dd354e..b295262 100644
--- a/kvm-all.c
+++ b/kvm-all.c
@@ -64,6 +64,7 @@ struct KVMState
int vmfd;
int coalesced_mmio;
struct kvm_coalesced_mmio_ring *coalesced_mmio_ring;
+ long mmap_size;
int broken_set_mem_region;
int migration_log;
int vcpu_events;
@@ -228,7 +229,7 @@ int kvm_init_vcpu(CPUState *env)
DPRINTF("KVM_GET_VCPU_MMAP_SIZE failed\n");
goto err;
}
-
+ env->kvm_state->mmap_size = mmap_size;
env->kvm_run = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED,
env->kvm_fd, 0);
if (env->kvm_run == MAP_FAILED) {
@@ -1026,6 +1027,13 @@ int kvm_cpu_exec(CPUState *env)
case KVM_EXIT_INTERNAL_ERROR:
ret = kvm_handle_internal_error(env, run);
break;
+ case KVM_EXIT_VCPU_DEAD:
+ ret = munmap(env->kvm_run, env->kvm_state->mmap_size);
+ ret = close(env->kvm_fd);
+ env->state = CPU_STATE_ZAPPED;
+ qemu_mutex_unlock_iothread();
+ goto out;
+ break;
default:
DPRINTF("kvm_arch_handle_exit\n");
ret = kvm_arch_handle_exit(env, run);
@@ -1033,6 +1041,7 @@ int kvm_cpu_exec(CPUState *env)
}
} while (ret == 0);

+out:
if (ret < 0) {
cpu_dump_state(env, stderr, fprintf, CPU_DUMP_CODE);
vm_stop(VMSTOP_PANIC);
diff --git a/monitor.c b/monitor.c
index cb485bf..51c8c52 100644
--- a/monitor.c
+++ b/monitor.c
@@ -971,11 +971,13 @@ static void do_cpu_set_nr(Monitor *mon, const QDict *qdict)
status = qdict_get_str(qdict, "state");
value = qdict_get_int(qdict, "cpu");

- if (!strcmp(status, "online"))
- state = 1;
- else if (!strcmp(status, "offline"))
- state = 0;
- else {
+ if (!strcmp(status, "online")) {
+ state = 2;
+ } else if (!strcmp(status, "offline")) {
+ state = 1;
+ } else if (!strcmp(status, "zap")) {
+ state = 0;
+ } else {
monitor_printf(mon, "invalid status: %s\n", status);
return;
}
--
1.7.4.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/