* [Qemu-devel] [RFC qom-cpu v3 01/10] x86: move apic_state field from CPUX86State to X86CPU
2013-09-16 2:40 [Qemu-devel] [RFC qom-cpu v3 00/10] i386: add cpu hot remove support Chen Fan
@ 2013-09-16 2:40 ` Chen Fan
2013-09-16 2:40 ` [Qemu-devel] [RFC qom-cpu v3 02/10] apic: remove redundant variable 'apic_no' from apic_init_common() Chen Fan
` (9 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Chen Fan @ 2013-09-16 2:40 UTC (permalink / raw)
To: qemu-devel; +Cc: Igor Mammedov, Andreas Färber
This motion is preparing for refactoring vCPU apic subsequently.
Signed-off-by: Chen Fan <chen.fan.fnst@cn.fujitsu.com>
---
cpu-exec.c | 2 +-
cpus.c | 5 ++---
hw/i386/kvmvapic.c | 8 +++-----
hw/i386/pc.c | 17 ++++++++---------
target-i386/cpu-qom.h | 4 ++++
target-i386/cpu.c | 22 ++++++++++------------
target-i386/cpu.h | 4 ----
target-i386/helper.c | 9 ++++-----
target-i386/kvm.c | 23 ++++++++++-------------
target-i386/misc_helper.c | 8 ++++----
10 files changed, 46 insertions(+), 56 deletions(-)
diff --git a/cpu-exec.c b/cpu-exec.c
index 301be28..463dc2e 100644
--- a/cpu-exec.c
+++ b/cpu-exec.c
@@ -320,7 +320,7 @@ int cpu_exec(CPUArchState *env)
#if !defined(CONFIG_USER_ONLY)
if (interrupt_request & CPU_INTERRUPT_POLL) {
cpu->interrupt_request &= ~CPU_INTERRUPT_POLL;
- apic_poll_irq(env->apic_state);
+ apic_poll_irq(x86_env_get_cpu(env)->apic_state);
}
#endif
if (interrupt_request & CPU_INTERRUPT_INIT) {
diff --git a/cpus.c b/cpus.c
index 980697e..3bc10f4 100644
--- a/cpus.c
+++ b/cpus.c
@@ -1383,12 +1383,11 @@ void qmp_inject_nmi(Error **errp)
CPU_FOREACH(cs) {
X86CPU *cpu = X86_CPU(cs);
- CPUX86State *env = &cpu->env;
- if (!env->apic_state) {
+ if (!cpu->apic_state) {
cpu_interrupt(cs, CPU_INTERRUPT_NMI);
} else {
- apic_deliver_nmi(env->apic_state);
+ apic_deliver_nmi(cpu->apic_state);
}
}
#else
diff --git a/hw/i386/kvmvapic.c b/hw/i386/kvmvapic.c
index d3a6fbe..5a01c35 100644
--- a/hw/i386/kvmvapic.c
+++ b/hw/i386/kvmvapic.c
@@ -366,7 +366,7 @@ static int vapic_enable(VAPICROMState *s, X86CPU *cpu)
(((hwaddr)cpu_number) << VAPIC_CPU_SHIFT);
cpu_physical_memory_rw(vapic_paddr + offsetof(VAPICState, enabled),
(void *)&enabled, sizeof(enabled), 1);
- apic_enable_vapic(cpu->env.apic_state, vapic_paddr);
+ apic_enable_vapic(cpu->apic_state, vapic_paddr);
s->state = VAPIC_ACTIVE;
@@ -496,12 +496,10 @@ static void vapic_enable_tpr_reporting(bool enable)
};
CPUState *cs;
X86CPU *cpu;
- CPUX86State *env;
CPU_FOREACH(cs) {
cpu = X86_CPU(cs);
- env = &cpu->env;
- info.apic = env->apic_state;
+ info.apic = cpu->apic_state;
run_on_cpu(cs, vapic_do_enable_tpr_reporting, &info);
}
}
@@ -690,7 +688,7 @@ static void vapic_write(void *opaque, hwaddr addr, uint64_t data,
default:
case 4:
if (!kvm_irqchip_in_kernel()) {
- apic_poll_irq(env->apic_state);
+ apic_poll_irq(cpu->apic_state);
}
break;
}
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 0c313fe..832c9b2 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -169,13 +169,14 @@ void cpu_smm_update(CPUX86State *env)
int cpu_get_pic_interrupt(CPUX86State *env)
{
int intno;
+ X86CPU *cpu = x86_env_get_cpu(env);
- intno = apic_get_interrupt(env->apic_state);
+ intno = apic_get_interrupt(cpu->apic_state);
if (intno >= 0) {
return intno;
}
/* read the irq from the PIC */
- if (!apic_accept_pic_intr(env->apic_state)) {
+ if (!apic_accept_pic_intr(cpu->apic_state)) {
return -1;
}
@@ -187,15 +188,13 @@ static void pic_irq_request(void *opaque, int irq, int level)
{
CPUState *cs = first_cpu;
X86CPU *cpu = X86_CPU(cs);
- CPUX86State *env = &cpu->env;
DPRINTF("pic_irqs: %s irq %d\n", level? "raise" : "lower", irq);
- if (env->apic_state) {
+ if (cpu->apic_state) {
CPU_FOREACH(cs) {
cpu = X86_CPU(cs);
- env = &cpu->env;
- if (apic_accept_pic_intr(env->apic_state)) {
- apic_deliver_pic_intr(env->apic_state, level);
+ if (apic_accept_pic_intr(cpu->apic_state)) {
+ apic_deliver_pic_intr(cpu->apic_state, level);
}
}
} else {
@@ -890,7 +889,7 @@ DeviceState *cpu_get_current_apic(void)
{
if (current_cpu) {
X86CPU *cpu = X86_CPU(current_cpu);
- return cpu->env.apic_state;
+ return cpu->apic_state;
} else {
return NULL;
}
@@ -984,7 +983,7 @@ void pc_cpus_init(const char *cpu_model, DeviceState *icc_bridge)
}
/* map APIC MMIO area if CPU has APIC */
- if (cpu && cpu->env.apic_state) {
+ if (cpu && cpu->apic_state) {
/* XXX: what if the base changes? */
sysbus_mmio_map_overlap(SYS_BUS_DEVICE(icc_bridge), 0,
APIC_DEFAULT_ADDRESS, 0x1000);
diff --git a/target-i386/cpu-qom.h b/target-i386/cpu-qom.h
index c4447c2..548a449 100644
--- a/target-i386/cpu-qom.h
+++ b/target-i386/cpu-qom.h
@@ -66,6 +66,10 @@ typedef struct X86CPU {
CPUX86State env;
+ /* in order to simplify APIC support, we leave this pointer to the
+ user */
+ struct DeviceState *apic_state;
+
bool hyperv_vapic;
bool hyperv_relaxed_timing;
int hyperv_spinlock_attempts;
diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index 42c5de0..047bb77 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -2259,7 +2259,7 @@ static void x86_cpu_reset(CPUState *s)
#if !defined(CONFIG_USER_ONLY)
/* We hard-wire the BSP to the first CPU. */
if (s->cpu_index == 0) {
- apic_designate_bsp(env->apic_state);
+ apic_designate_bsp(cpu->apic_state);
}
s->halted = !cpu_is_bsp(cpu);
@@ -2269,7 +2269,7 @@ static void x86_cpu_reset(CPUState *s)
#ifndef CONFIG_USER_ONLY
bool cpu_is_bsp(X86CPU *cpu)
{
- return cpu_get_apic_base(cpu->env.apic_state) & MSR_IA32_APICBASE_BSP;
+ return cpu_get_apic_base(cpu->apic_state) & MSR_IA32_APICBASE_BSP;
}
/* TODO: remove me, when reset over QOM tree is implemented */
@@ -2310,31 +2310,29 @@ static void x86_cpu_apic_create(X86CPU *cpu, Error **errp)
apic_type = "xen-apic";
}
- env->apic_state = qdev_try_create(qdev_get_parent_bus(dev), apic_type);
- if (env->apic_state == NULL) {
+ cpu->apic_state = qdev_try_create(qdev_get_parent_bus(dev), apic_type);
+ if (cpu->apic_state == NULL) {
error_setg(errp, "APIC device '%s' could not be created", apic_type);
return;
}
object_property_add_child(OBJECT(cpu), "apic",
- OBJECT(env->apic_state), NULL);
- qdev_prop_set_uint8(env->apic_state, "id", env->cpuid_apic_id);
+ OBJECT(cpu->apic_state), NULL);
+ qdev_prop_set_uint8(cpu->apic_state, "id", env->cpuid_apic_id);
/* TODO: convert to link<> */
- apic = APIC_COMMON(env->apic_state);
+ apic = APIC_COMMON(cpu->apic_state);
apic->cpu = cpu;
}
static void x86_cpu_apic_realize(X86CPU *cpu, Error **errp)
{
- CPUX86State *env = &cpu->env;
-
- if (env->apic_state == NULL) {
+ if (cpu->apic_state == NULL) {
return;
}
- if (qdev_init(env->apic_state)) {
+ if (qdev_init(cpu->apic_state)) {
error_setg(errp, "APIC device '%s' could not be initialized",
- object_get_typename(OBJECT(env->apic_state)));
+ object_get_typename(OBJECT(cpu->apic_state)));
return;
}
}
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index 8a3d0fd..8b973d2 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -866,10 +866,6 @@ typedef struct CPUX86State {
int tsc_khz;
void *kvm_xsave_buf;
- /* in order to simplify APIC support, we leave this pointer to the
- user */
- struct DeviceState *apic_state;
-
uint64_t mcg_cap;
uint64_t mcg_ctl;
uint64_t mce_banks[MCE_BANKS_DEF*4];
diff --git a/target-i386/helper.c b/target-i386/helper.c
index 7c58e27..3227454 100644
--- a/target-i386/helper.c
+++ b/target-i386/helper.c
@@ -1250,7 +1250,8 @@ void cpu_report_tpr_access(CPUX86State *env, TPRAccess access)
} else {
cpu_restore_state(env, env->mem_io_pc);
- apic_handle_tpr_access_report(env->apic_state, env->eip, access);
+ apic_handle_tpr_access_report(x86_env_get_cpu(env)->apic_state,
+ env->eip, access);
}
}
#endif /* !CONFIG_USER_ONLY */
@@ -1297,14 +1298,12 @@ void do_cpu_init(X86CPU *cpu)
cpu_reset(cs);
cs->interrupt_request = sipi;
env->pat = pat;
- apic_init_reset(env->apic_state);
+ apic_init_reset(cpu->apic_state);
}
void do_cpu_sipi(X86CPU *cpu)
{
- CPUX86State *env = &cpu->env;
-
- apic_sipi(env->apic_state);
+ apic_sipi(cpu->apic_state);
}
#else
void do_cpu_init(X86CPU *cpu)
diff --git a/target-i386/kvm.c b/target-i386/kvm.c
index 0b6eb01..9f5a98a 100644
--- a/target-i386/kvm.c
+++ b/target-i386/kvm.c
@@ -1045,8 +1045,8 @@ static int kvm_put_sregs(X86CPU *cpu)
sregs.cr3 = env->cr[3];
sregs.cr4 = env->cr[4];
- sregs.cr8 = cpu_get_apic_tpr(env->apic_state);
- sregs.apic_base = cpu_get_apic_base(env->apic_state);
+ sregs.cr8 = cpu_get_apic_tpr(cpu->apic_state);
+ sregs.apic_base = cpu_get_apic_base(cpu->apic_state);
sregs.efer = env->efer;
@@ -1507,8 +1507,7 @@ static int kvm_get_mp_state(X86CPU *cpu)
static int kvm_get_apic(X86CPU *cpu)
{
- CPUX86State *env = &cpu->env;
- DeviceState *apic = env->apic_state;
+ DeviceState *apic = cpu->apic_state;
struct kvm_lapic_state kapic;
int ret;
@@ -1525,8 +1524,7 @@ static int kvm_get_apic(X86CPU *cpu)
static int kvm_put_apic(X86CPU *cpu)
{
- CPUX86State *env = &cpu->env;
- DeviceState *apic = env->apic_state;
+ DeviceState *apic = cpu->apic_state;
struct kvm_lapic_state kapic;
if (apic && kvm_irqchip_in_kernel()) {
@@ -1844,7 +1842,7 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run)
}
DPRINTF("setting tpr\n");
- run->cr8 = cpu_get_apic_tpr(env->apic_state);
+ run->cr8 = cpu_get_apic_tpr(x86_cpu->apic_state);
}
}
@@ -1858,8 +1856,8 @@ void kvm_arch_post_run(CPUState *cpu, struct kvm_run *run)
} else {
env->eflags &= ~IF_MASK;
}
- cpu_set_apic_tpr(env->apic_state, run->cr8);
- cpu_set_apic_base(env->apic_state, run->apic_base);
+ cpu_set_apic_tpr(x86_cpu->apic_state, run->cr8);
+ cpu_set_apic_base(x86_cpu->apic_state, run->apic_base);
}
int kvm_arch_process_async_events(CPUState *cs)
@@ -1896,7 +1894,7 @@ int kvm_arch_process_async_events(CPUState *cs)
if (cs->interrupt_request & CPU_INTERRUPT_POLL) {
cs->interrupt_request &= ~CPU_INTERRUPT_POLL;
- apic_poll_irq(env->apic_state);
+ apic_poll_irq(cpu->apic_state);
}
if (((cs->interrupt_request & CPU_INTERRUPT_HARD) &&
(env->eflags & IF_MASK)) ||
@@ -1914,7 +1912,7 @@ int kvm_arch_process_async_events(CPUState *cs)
if (cs->interrupt_request & CPU_INTERRUPT_TPR) {
cs->interrupt_request &= ~CPU_INTERRUPT_TPR;
kvm_cpu_synchronize_state(cs);
- apic_handle_tpr_access_report(env->apic_state, env->eip,
+ apic_handle_tpr_access_report(cpu->apic_state, env->eip,
env->tpr_access_type);
}
@@ -1938,11 +1936,10 @@ static int kvm_handle_halt(X86CPU *cpu)
static int kvm_handle_tpr_access(X86CPU *cpu)
{
- CPUX86State *env = &cpu->env;
CPUState *cs = CPU(cpu);
struct kvm_run *run = cs->kvm_run;
- apic_handle_tpr_access_report(env->apic_state, run->tpr_access.rip,
+ apic_handle_tpr_access_report(cpu->apic_state, run->tpr_access.rip,
run->tpr_access.is_write ? TPR_ACCESS_WRITE
: TPR_ACCESS_READ);
return 1;
diff --git a/target-i386/misc_helper.c b/target-i386/misc_helper.c
index 93933fd..52424f4 100644
--- a/target-i386/misc_helper.c
+++ b/target-i386/misc_helper.c
@@ -155,7 +155,7 @@ target_ulong helper_read_crN(CPUX86State *env, int reg)
break;
case 8:
if (!(env->hflags2 & HF2_VINTR_MASK)) {
- val = cpu_get_apic_tpr(env->apic_state);
+ val = cpu_get_apic_tpr(x86_env_get_cpu(env)->apic_state);
} else {
val = env->v_tpr;
}
@@ -179,7 +179,7 @@ void helper_write_crN(CPUX86State *env, int reg, target_ulong t0)
break;
case 8:
if (!(env->hflags2 & HF2_VINTR_MASK)) {
- cpu_set_apic_tpr(env->apic_state, t0);
+ cpu_set_apic_tpr(x86_env_get_cpu(env)->apic_state, t0);
}
env->v_tpr = t0 & 0x0f;
break;
@@ -286,7 +286,7 @@ void helper_wrmsr(CPUX86State *env)
env->sysenter_eip = val;
break;
case MSR_IA32_APICBASE:
- cpu_set_apic_base(env->apic_state, val);
+ cpu_set_apic_base(x86_env_get_cpu(env)->apic_state, val);
break;
case MSR_EFER:
{
@@ -437,7 +437,7 @@ void helper_rdmsr(CPUX86State *env)
val = env->sysenter_eip;
break;
case MSR_IA32_APICBASE:
- val = cpu_get_apic_base(env->apic_state);
+ val = cpu_get_apic_base(x86_env_get_cpu(env)->apic_state);
break;
case MSR_EFER:
val = env->efer;
--
1.8.1.4
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [Qemu-devel] [RFC qom-cpu v3 02/10] apic: remove redundant variable 'apic_no' from apic_init_common()
2013-09-16 2:40 [Qemu-devel] [RFC qom-cpu v3 00/10] i386: add cpu hot remove support Chen Fan
2013-09-16 2:40 ` [Qemu-devel] [RFC qom-cpu v3 01/10] x86: move apic_state field from CPUX86State to X86CPU Chen Fan
@ 2013-09-16 2:40 ` Chen Fan
2013-09-16 2:40 ` [Qemu-devel] [RFC qom-cpu v3 03/10] apic: remove local_apics array and using CPU_FOREACH instead Chen Fan
` (8 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Chen Fan @ 2013-09-16 2:40 UTC (permalink / raw)
To: qemu-devel; +Cc: Igor Mammedov, Andreas Färber
In struct APICCommonState, there is an id field yet, which was set earlier,
qdev_prop_set_uint8(env->apic_state, "id", env->cpuid_apic_id);
so we use the id field instead of the variable 'apic_no' to represent the unique apic
index.
Signed-off-by: Chen Fan <chen.fan.fnst@cn.fujitsu.com>
---
hw/intc/apic_common.c | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c
index a0beb10..82fbb7f 100644
--- a/hw/intc/apic_common.c
+++ b/hw/intc/apic_common.c
@@ -289,13 +289,9 @@ static int apic_init_common(ICCDevice *dev)
APICCommonState *s = APIC_COMMON(dev);
APICCommonClass *info;
static DeviceState *vapic;
- static int apic_no;
static bool mmio_registered;
- if (apic_no >= MAX_APICS) {
- return -1;
- }
- s->idx = apic_no++;
+ s->idx = s->id;
info = APIC_COMMON_GET_CLASS(s);
info->init(s);
--
1.8.1.4
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [Qemu-devel] [RFC qom-cpu v3 03/10] apic: remove local_apics array and using CPU_FOREACH instead
2013-09-16 2:40 [Qemu-devel] [RFC qom-cpu v3 00/10] i386: add cpu hot remove support Chen Fan
2013-09-16 2:40 ` [Qemu-devel] [RFC qom-cpu v3 01/10] x86: move apic_state field from CPUX86State to X86CPU Chen Fan
2013-09-16 2:40 ` [Qemu-devel] [RFC qom-cpu v3 02/10] apic: remove redundant variable 'apic_no' from apic_init_common() Chen Fan
@ 2013-09-16 2:40 ` Chen Fan
2013-09-16 2:40 ` [Qemu-devel] [RFC qom-cpu v3 04/10] x86: add x86_cpu_unrealizefn() for cpu apic remove Chen Fan
` (7 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Chen Fan @ 2013-09-16 2:40 UTC (permalink / raw)
To: qemu-devel; +Cc: Igor Mammedov, Andreas Färber
Using CPU_FOREACH() marco instead of scaning the entire
local_apics array for fast searching apic.
Signed-off-by: Chen Fan <chen.fan.fnst@cn.fujitsu.com>
---
hw/intc/apic.c | 73 ++++++++++++++++++-----------------------
include/hw/i386/apic_internal.h | 2 --
2 files changed, 32 insertions(+), 43 deletions(-)
diff --git a/hw/intc/apic.c b/hw/intc/apic.c
index a913186..f8f2cbf 100644
--- a/hw/intc/apic.c
+++ b/hw/intc/apic.c
@@ -32,8 +32,6 @@
#define SYNC_TO_VAPIC 0x2
#define SYNC_ISR_IRR_TO_VAPIC 0x4
-static APICCommonState *local_apics[MAX_APICS + 1];
-
static void apic_set_irq(APICCommonState *s, int vector_num, int trigger_mode);
static void apic_update_irq(APICCommonState *s);
static void apic_get_delivery_bitmask(uint32_t *deliver_bitmask,
@@ -200,18 +198,15 @@ static void apic_external_nmi(APICCommonState *s)
#define foreach_apic(apic, deliver_bitmask, code) \
{\
+ CPUState *cpu;\
int __i, __j, __mask;\
- for(__i = 0; __i < MAX_APIC_WORDS; __i++) {\
+ CPU_FOREACH(cpu) {\
+ apic = APIC_COMMON(X86_CPU(cpu)->apic_state);\
+ __i = apic->idx / 32;\
+ __j = apic->idx % 32;\
__mask = deliver_bitmask[__i];\
- if (__mask) {\
- for(__j = 0; __j < 32; __j++) {\
- if (__mask & (1 << __j)) {\
- apic = local_apics[__i * 32 + __j];\
- if (apic) {\
- code;\
- }\
- }\
- }\
+ if (__mask & (1 << __j)) {\
+ code;\
}\
}\
}
@@ -235,9 +230,13 @@ static void apic_bus_deliver(const uint32_t *deliver_bitmask,
}
}
if (d >= 0) {
- apic_iter = local_apics[d];
- if (apic_iter) {
- apic_set_irq(apic_iter, vector_num, trigger_mode);
+ CPUState *cpu;
+ CPU_FOREACH(cpu) {
+ apic_iter = APIC_COMMON(X86_CPU(cpu)->apic_state);
+ if (apic_iter->idx == d) {
+ apic_set_irq(apic_iter, vector_num, trigger_mode);
+ break;
+ }
}
}
}
@@ -422,18 +421,14 @@ static void apic_eoi(APICCommonState *s)
static int apic_find_dest(uint8_t dest)
{
- APICCommonState *apic = local_apics[dest];
- int i;
-
- if (apic && apic->id == dest)
- return dest; /* shortcut in case apic->id == apic->idx */
+ APICCommonState *apic;
+ CPUState *cpu;
- for (i = 0; i < MAX_APICS; i++) {
- apic = local_apics[i];
- if (apic && apic->id == dest)
- return i;
- if (!apic)
- break;
+ CPU_FOREACH(cpu) {
+ apic = APIC_COMMON(X86_CPU(cpu)->apic_state);
+ if (apic->id == dest) {
+ return apic->idx;
+ }
}
return -1;
@@ -443,7 +438,7 @@ static void apic_get_delivery_bitmask(uint32_t *deliver_bitmask,
uint8_t dest, uint8_t dest_mode)
{
APICCommonState *apic_iter;
- int i;
+ CPUState *cpu;
if (dest_mode == 0) {
if (dest == 0xff) {
@@ -457,20 +452,17 @@ static void apic_get_delivery_bitmask(uint32_t *deliver_bitmask,
} else {
/* XXX: cluster mode */
memset(deliver_bitmask, 0x00, MAX_APIC_WORDS * sizeof(uint32_t));
- for(i = 0; i < MAX_APICS; i++) {
- apic_iter = local_apics[i];
- if (apic_iter) {
- if (apic_iter->dest_mode == 0xf) {
- if (dest & apic_iter->log_dest)
- apic_set_bit(deliver_bitmask, i);
- } else if (apic_iter->dest_mode == 0x0) {
- if ((dest & 0xf0) == (apic_iter->log_dest & 0xf0) &&
- (dest & apic_iter->log_dest & 0x0f)) {
- apic_set_bit(deliver_bitmask, i);
- }
+ CPU_FOREACH(cpu) {
+ apic_iter = APIC_COMMON(X86_CPU(cpu)->apic_state);
+ if (apic_iter->dest_mode == 0xf) {
+ if (dest & apic_iter->log_dest) {
+ apic_set_bit(deliver_bitmask, apic_iter->idx);
+ }
+ } else if (apic_iter->dest_mode == 0x0) {
+ if ((dest & 0xf0) == (apic_iter->log_dest & 0xf0) &&
+ (dest & apic_iter->log_dest & 0x0f)) {
+ apic_set_bit(deliver_bitmask, apic_iter->idx);
}
- } else {
- break;
}
}
}
@@ -877,7 +869,6 @@ static void apic_init(APICCommonState *s)
APIC_SPACE_SIZE);
s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, apic_timer, s);
- local_apics[s->idx] = s;
msi_supported = true;
}
diff --git a/include/hw/i386/apic_internal.h b/include/hw/i386/apic_internal.h
index 1b0a7fb..5b763ac 100644
--- a/include/hw/i386/apic_internal.h
+++ b/include/hw/i386/apic_internal.h
@@ -64,8 +64,6 @@
#define VAPIC_ENABLE_BIT 0
#define VAPIC_ENABLE_MASK (1 << VAPIC_ENABLE_BIT)
-#define MAX_APICS 255
-
typedef struct APICCommonState APICCommonState;
#define TYPE_APIC_COMMON "apic-common"
--
1.8.1.4
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [Qemu-devel] [RFC qom-cpu v3 04/10] x86: add x86_cpu_unrealizefn() for cpu apic remove
2013-09-16 2:40 [Qemu-devel] [RFC qom-cpu v3 00/10] i386: add cpu hot remove support Chen Fan
` (2 preceding siblings ...)
2013-09-16 2:40 ` [Qemu-devel] [RFC qom-cpu v3 03/10] apic: remove local_apics array and using CPU_FOREACH instead Chen Fan
@ 2013-09-16 2:40 ` Chen Fan
2013-09-16 2:40 ` [Qemu-devel] [RFC qom-cpu v3 05/10] qmp: add 'cpu-del' command support Chen Fan
` (6 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Chen Fan @ 2013-09-16 2:40 UTC (permalink / raw)
To: qemu-devel; +Cc: Igor Mammedov, Andreas Färber
Implement x86_cpu_unrealizefn() for corresponding x86_cpu_realizefn(),
which is mostly used to clear the apic related information at here.
Signed-off-by: Chen Fan <chen.fan.fnst@cn.fujitsu.com>
---
hw/i386/kvm/apic.c | 8 ++++++++
hw/intc/apic.c | 8 ++++++++
target-i386/cpu-qom.h | 1 +
target-i386/cpu.c | 35 +++++++++++++++++++++++++++++++++++
4 files changed, 52 insertions(+)
diff --git a/hw/i386/kvm/apic.c b/hw/i386/kvm/apic.c
index 5609063..9461600 100644
--- a/hw/i386/kvm/apic.c
+++ b/hw/i386/kvm/apic.c
@@ -181,11 +181,19 @@ static void kvm_apic_init(APICCommonState *s)
}
}
+static void kvm_apic_unrealize(DeviceState *dev, Error **errp)
+{
+ APICCommonState *s = APIC_COMMON(dev);
+ memory_region_destroy(&s->io_memory);
+}
+
static void kvm_apic_class_init(ObjectClass *klass, void *data)
{
APICCommonClass *k = APIC_COMMON_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
k->init = kvm_apic_init;
+ dc->unrealize = kvm_apic_unrealize;
k->set_base = kvm_apic_set_base;
k->set_tpr = kvm_apic_set_tpr;
k->get_tpr = kvm_apic_get_tpr;
diff --git a/hw/intc/apic.c b/hw/intc/apic.c
index f8f2cbf..46ea047 100644
--- a/hw/intc/apic.c
+++ b/hw/intc/apic.c
@@ -873,11 +873,19 @@ static void apic_init(APICCommonState *s)
msi_supported = true;
}
+static void apic_unrealize(DeviceState *dev, Error **errp)
+{
+ APICCommonState *s = APIC_COMMON(dev);
+ memory_region_destroy(&s->io_memory);
+}
+
static void apic_class_init(ObjectClass *klass, void *data)
{
APICCommonClass *k = APIC_COMMON_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
k->init = apic_init;
+ dc->unrealize = apic_unrealize;
k->set_base = apic_set_base;
k->set_tpr = apic_set_tpr;
k->get_tpr = apic_get_tpr;
diff --git a/target-i386/cpu-qom.h b/target-i386/cpu-qom.h
index 548a449..ad8ad82 100644
--- a/target-i386/cpu-qom.h
+++ b/target-i386/cpu-qom.h
@@ -50,6 +50,7 @@ typedef struct X86CPUClass {
/*< public >*/
DeviceRealize parent_realize;
+ DeviceUnrealize parent_unrealize;
void (*parent_reset)(CPUState *cpu);
} X86CPUClass;
diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index 047bb77..6ac3ff2 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -2336,10 +2336,31 @@ static void x86_cpu_apic_realize(X86CPU *cpu, Error **errp)
return;
}
}
+
+static void x86_cpu_apic_unrealize(X86CPU *cpu, Error **errp)
+{
+ Error *local_err = NULL;
+
+ if (cpu->apic_state == NULL) {
+ return;
+ }
+
+ object_property_set_bool(OBJECT(cpu->apic_state),
+ false, "realized", &local_err);
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ qdev_free(cpu->apic_state);
+}
#else
static void x86_cpu_apic_realize(X86CPU *cpu, Error **errp)
{
}
+static void x86_cpu_apic_unrealize(X86CPU *cpu, Error **errp)
+{
+}
#endif
static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
@@ -2415,6 +2436,18 @@ out:
}
}
+static void x86_cpu_unrealizefn(DeviceState *dev, Error **errp)
+{
+ X86CPU *cpu = X86_CPU(dev);
+ Error *local_err = NULL;
+
+ x86_cpu_apic_unrealize(cpu, &local_err);
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
+ return;
+ }
+}
+
/* Enables contiguous-apic-ID mode, for compatibility */
static bool compat_apic_id_mode;
@@ -2546,7 +2579,9 @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data)
DeviceClass *dc = DEVICE_CLASS(oc);
xcc->parent_realize = dc->realize;
+ xcc->parent_unrealize = dc->unrealize;
dc->realize = x86_cpu_realizefn;
+ dc->unrealize = x86_cpu_unrealizefn;
dc->bus_type = TYPE_ICC_BUS;
dc->props = x86_cpu_properties;
--
1.8.1.4
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [Qemu-devel] [RFC qom-cpu v3 05/10] qmp: add 'cpu-del' command support
2013-09-16 2:40 [Qemu-devel] [RFC qom-cpu v3 00/10] i386: add cpu hot remove support Chen Fan
` (3 preceding siblings ...)
2013-09-16 2:40 ` [Qemu-devel] [RFC qom-cpu v3 04/10] x86: add x86_cpu_unrealizefn() for cpu apic remove Chen Fan
@ 2013-09-16 2:40 ` Chen Fan
2013-09-16 2:40 ` [Qemu-devel] [RFC qom-cpu v3 06/10] qom cpu: rename variable 'cpu_added_notifier' to 'cpu_hotplug_notifier' Chen Fan
` (5 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Chen Fan @ 2013-09-16 2:40 UTC (permalink / raw)
To: qemu-devel; +Cc: Igor Mammedov, Andreas Färber
Signed-off-by: Chen Fan <chen.fan.fnst@cn.fujitsu.com>
---
hw/i386/pc.c | 6 ++++++
hw/i386/pc_piix.c | 1 +
include/hw/boards.h | 2 ++
include/hw/i386/pc.h | 1 +
qapi-schema.json | 12 ++++++++++++
qmp-commands.hx | 23 +++++++++++++++++++++++
qmp.c | 9 +++++++++
7 files changed, 54 insertions(+)
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 832c9b2..40d611e 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -956,6 +956,12 @@ void pc_hot_add_cpu(const int64_t id, Error **errp)
pc_new_cpu(current_cpu_model, apic_id, icc_bridge, errp);
}
+void pc_hot_del_cpu(const int64_t id, Error **errp)
+{
+ /* TODO: hot remove vCPU. */
+ error_setg(errp, "Hot-remove CPU is not supported.");
+}
+
void pc_cpus_init(const char *cpu_model, DeviceState *icc_bridge)
{
int i;
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index 6e1e654..d779b75 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -347,6 +347,7 @@ static QEMUMachine pc_i440fx_machine_v1_6 = {
.desc = "Standard PC (i440FX + PIIX, 1996)",
.init = pc_init_pci_1_6,
.hot_add_cpu = pc_hot_add_cpu,
+ .hot_del_cpu = pc_hot_del_cpu,
.max_cpus = 255,
.is_default = 1,
DEFAULT_MACHINE_OPTIONS,
diff --git a/include/hw/boards.h b/include/hw/boards.h
index fb7c6f1..fea3737 100644
--- a/include/hw/boards.h
+++ b/include/hw/boards.h
@@ -23,6 +23,7 @@ typedef void QEMUMachineInitFunc(QEMUMachineInitArgs *args);
typedef void QEMUMachineResetFunc(void);
typedef void QEMUMachineHotAddCPUFunc(const int64_t id, Error **errp);
+typedef void QEMUMachineHotDelCPUFunc(const int64_t id, Error **errp);
typedef struct QEMUMachine {
const char *name;
@@ -31,6 +32,7 @@ typedef struct QEMUMachine {
QEMUMachineInitFunc *init;
QEMUMachineResetFunc *reset;
QEMUMachineHotAddCPUFunc *hot_add_cpu;
+ QEMUMachineHotDelCPUFunc *hot_del_cpu;
BlockInterfaceType block_default_type;
int max_cpus;
unsigned int no_serial:1,
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index f79d478..b7e66f4 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -96,6 +96,7 @@ void pc_acpi_smi_interrupt(void *opaque, int irq, int level);
void pc_cpus_init(const char *cpu_model, DeviceState *icc_bridge);
void pc_hot_add_cpu(const int64_t id, Error **errp);
+void pc_hot_del_cpu(const int64_t id, Error **errp);
void pc_acpi_init(const char *default_dsdt);
PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size,
diff --git a/qapi-schema.json b/qapi-schema.json
index a51f7d2..6052aa9 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1432,6 +1432,18 @@
##
{ 'command': 'cpu-add', 'data': {'id': 'int'} }
+# @cpu-del
+
+# Deletes CPU with specified ID
+#
+# @id: ID of CPU to be deleted, valid values [0..max_cpus)
+#
+# Returns: Nothing on success
+#
+# Since 1.7
+##
+{ 'command': 'cpu-del', 'data': {'id': 'int'} }
+
##
# @memsave:
#
diff --git a/qmp-commands.hx b/qmp-commands.hx
index cf47e3f..16b54fd 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -411,6 +411,29 @@ Example:
EQMP
{
+ .name = "cpu-del",
+ .args_type = "id:i",
+ .mhandler.cmd_new = qmp_marshal_input_cpu_del,
+ },
+
+SQMP
+cpu-del
+-------
+
+Deletes virtual cpu
+
+Arguments:
+
+- "id": cpu id (json-int)
+
+Example:
+
+-> { "execute": "cpu-del", "arguments": { "id": 2 } }
+<- { "return": {} }
+
+EQMP
+
+ {
.name = "memsave",
.args_type = "val:l,size:i,filename:s,cpu:i?",
.mhandler.cmd_new = qmp_marshal_input_memsave,
diff --git a/qmp.c b/qmp.c
index 4c149b3..84dc873 100644
--- a/qmp.c
+++ b/qmp.c
@@ -118,6 +118,15 @@ void qmp_cpu_add(int64_t id, Error **errp)
}
}
+void qmp_cpu_del(int64_t id, Error **errp)
+{
+ if (current_machine->hot_del_cpu) {
+ current_machine->hot_del_cpu(id, errp);
+ } else {
+ error_setg(errp, "Not supported");
+ }
+}
+
#ifndef CONFIG_VNC
/* If VNC support is enabled, the "true" query-vnc command is
defined in the VNC subsystem */
--
1.8.1.4
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [Qemu-devel] [RFC qom-cpu v3 06/10] qom cpu: rename variable 'cpu_added_notifier' to 'cpu_hotplug_notifier'
2013-09-16 2:40 [Qemu-devel] [RFC qom-cpu v3 00/10] i386: add cpu hot remove support Chen Fan
` (4 preceding siblings ...)
2013-09-16 2:40 ` [Qemu-devel] [RFC qom-cpu v3 05/10] qmp: add 'cpu-del' command support Chen Fan
@ 2013-09-16 2:40 ` Chen Fan
2013-09-16 2:40 ` [Qemu-devel] [RFC qom-cpu v3 07/10] qom cpu: add UNPLUG cpu notifier support Chen Fan
` (4 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Chen Fan @ 2013-09-16 2:40 UTC (permalink / raw)
To: qemu-devel; +Cc: Igor Mammedov, Andreas Färber
Rename variable 'cpu_added_notifier' to 'cpu_hotplug_notifier', for
adding vcpu-remove notifier support.
Signed-off-by: Chen Fan <chen.fan.fnst@cn.fujitsu.com>
---
hw/acpi/piix4.c | 10 +++++-----
hw/i386/pc.c | 2 +-
include/sysemu/sysemu.h | 2 +-
qom/cpu.c | 10 +++++-----
4 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c
index 0b8d1d9..c8f4182 100644
--- a/hw/acpi/piix4.c
+++ b/hw/acpi/piix4.c
@@ -95,7 +95,7 @@ typedef struct PIIX4PMState {
uint8_t s4_val;
CPUStatus gpe_cpu;
- Notifier cpu_added_notifier;
+ Notifier cpu_hotplug_notifier;
} PIIX4PMState;
#define TYPE_PIIX4_PM "PIIX4_PM"
@@ -660,9 +660,9 @@ static void piix4_cpu_hotplug_req(PIIX4PMState *s, CPUState *cpu,
pm_update_sci(s);
}
-static void piix4_cpu_added_req(Notifier *n, void *opaque)
+static void piix4_cpu_hotplug(Notifier *n, void *opaque)
{
- PIIX4PMState *s = container_of(n, PIIX4PMState, cpu_added_notifier);
+ PIIX4PMState *s = container_of(n, PIIX4PMState, cpu_hotplug_notifier);
piix4_cpu_hotplug_req(s, CPU(opaque), PLUG);
}
@@ -695,8 +695,8 @@ static void piix4_acpi_system_hot_add_init(MemoryRegion *parent,
memory_region_init_io(&s->io_cpu, OBJECT(s), &cpu_hotplug_ops, s,
"acpi-cpu-hotplug", PIIX4_PROC_LEN);
memory_region_add_subregion(parent, PIIX4_PROC_BASE, &s->io_cpu);
- s->cpu_added_notifier.notify = piix4_cpu_added_req;
- qemu_register_cpu_added_notifier(&s->cpu_added_notifier);
+ s->cpu_hotplug_notifier.notify = piix4_cpu_hotplug;
+ qemu_register_cpu_hotplug_notifier(&s->cpu_hotplug_notifier);
}
static void enable_device(PIIX4PMState *s, int slot)
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 40d611e..8ab6e4f 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -406,7 +406,7 @@ void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
/* init CPU hotplug notifier */
cpu_hotplug_cb.rtc_state = s;
cpu_hotplug_cb.cpu_added_notifier.notify = rtc_notify_cpu_added;
- qemu_register_cpu_added_notifier(&cpu_hotplug_cb.cpu_added_notifier);
+ qemu_register_cpu_hotplug_notifier(&cpu_hotplug_cb.cpu_added_notifier);
if (set_boot_dev(s, boot_device)) {
exit(1);
diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
index b1aa059..e1c1120 100644
--- a/include/sysemu/sysemu.h
+++ b/include/sysemu/sysemu.h
@@ -153,7 +153,7 @@ void do_pci_device_hot_remove(Monitor *mon, const QDict *qdict);
void drive_hot_add(Monitor *mon, const QDict *qdict);
/* CPU hotplug */
-void qemu_register_cpu_added_notifier(Notifier *notifier);
+void qemu_register_cpu_hotplug_notifier(Notifier *notifier);
/* pcie aer error injection */
void pcie_aer_inject_error_print(Monitor *mon, const QObject *data);
diff --git a/qom/cpu.c b/qom/cpu.c
index fa7ec6b..7992fe1 100644
--- a/qom/cpu.c
+++ b/qom/cpu.c
@@ -67,12 +67,12 @@ static void cpu_common_get_memory_mapping(CPUState *cpu,
}
/* CPU hot-plug notifiers */
-static NotifierList cpu_added_notifiers =
- NOTIFIER_LIST_INITIALIZER(cpu_add_notifiers);
+static NotifierList cpu_hotplug_notifiers =
+ NOTIFIER_LIST_INITIALIZER(cpu_hotplug_notifiers);
-void qemu_register_cpu_added_notifier(Notifier *notifier)
+void qemu_register_cpu_hotplug_notifier(Notifier *notifier)
{
- notifier_list_add(&cpu_added_notifiers, notifier);
+ notifier_list_add(&cpu_hotplug_notifiers, notifier);
}
void cpu_reset_interrupt(CPUState *cpu, int mask)
@@ -218,7 +218,7 @@ static void cpu_common_realizefn(DeviceState *dev, Error **errp)
if (dev->hotplugged) {
cpu_synchronize_post_init(cpu);
- notifier_list_notify(&cpu_added_notifiers, dev);
+ notifier_list_notify(&cpu_hotplug_notifiers, dev);
cpu_resume(cpu);
}
}
--
1.8.1.4
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [Qemu-devel] [RFC qom-cpu v3 07/10] qom cpu: add UNPLUG cpu notifier support
2013-09-16 2:40 [Qemu-devel] [RFC qom-cpu v3 00/10] i386: add cpu hot remove support Chen Fan
` (5 preceding siblings ...)
2013-09-16 2:40 ` [Qemu-devel] [RFC qom-cpu v3 06/10] qom cpu: rename variable 'cpu_added_notifier' to 'cpu_hotplug_notifier' Chen Fan
@ 2013-09-16 2:40 ` Chen Fan
2013-09-16 2:40 ` [Qemu-devel] [RFC qom-cpu v3 08/10] i386: implement pc interface pc_hot_del_cpu() Chen Fan
` (3 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Chen Fan @ 2013-09-16 2:40 UTC (permalink / raw)
To: qemu-devel; +Cc: Igor Mammedov, Andreas Färber
Move struct HotplugEventType from file piix4.c to file qom/cpu.c,
and add struct CPUNotifier for supporting UNPLUG cpu notifier.
Signed-off-by: Chen Fan <chen.fan.fnst@cn.fujitsu.com>
---
hw/acpi/piix4.c | 8 ++------
include/qom/cpu.h | 10 ++++++++++
qom/cpu.c | 6 +++++-
3 files changed, 17 insertions(+), 7 deletions(-)
diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c
index c8f4182..2ddc9a8 100644
--- a/hw/acpi/piix4.c
+++ b/hw/acpi/piix4.c
@@ -635,11 +635,6 @@ static const MemoryRegionOps cpu_hotplug_ops = {
},
};
-typedef enum {
- PLUG,
- UNPLUG,
-} HotplugEventType;
-
static void piix4_cpu_hotplug_req(PIIX4PMState *s, CPUState *cpu,
HotplugEventType action)
{
@@ -663,8 +658,9 @@ static void piix4_cpu_hotplug_req(PIIX4PMState *s, CPUState *cpu,
static void piix4_cpu_hotplug(Notifier *n, void *opaque)
{
PIIX4PMState *s = container_of(n, PIIX4PMState, cpu_hotplug_notifier);
+ CPUNotifier *notifier = opaque;
- piix4_cpu_hotplug_req(s, CPU(opaque), PLUG);
+ piix4_cpu_hotplug_req(s, CPU(notifier->dev), notifier->type);
}
static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev,
diff --git a/include/qom/cpu.h b/include/qom/cpu.h
index 7739e00..0238532 100644
--- a/include/qom/cpu.h
+++ b/include/qom/cpu.h
@@ -507,6 +507,16 @@ void qemu_init_vcpu(CPUState *cpu);
*/
void cpu_single_step(CPUState *cpu, int enabled);
+typedef enum {
+ PLUG,
+ UNPLUG,
+} HotplugEventType;
+
+typedef struct CPUNotifier {
+ DeviceState *dev;
+ HotplugEventType type;
+} CPUNotifier;
+
#ifdef CONFIG_SOFTMMU
extern const struct VMStateDescription vmstate_cpu_common;
#else
diff --git a/qom/cpu.c b/qom/cpu.c
index 7992fe1..c6d7ebc 100644
--- a/qom/cpu.c
+++ b/qom/cpu.c
@@ -215,10 +215,14 @@ static ObjectClass *cpu_common_class_by_name(const char *cpu_model)
static void cpu_common_realizefn(DeviceState *dev, Error **errp)
{
CPUState *cpu = CPU(dev);
+ CPUNotifier notifier;
+
+ notifier.dev = dev;
+ notifier.type = PLUG;
if (dev->hotplugged) {
cpu_synchronize_post_init(cpu);
- notifier_list_notify(&cpu_hotplug_notifiers, dev);
+ notifier_list_notify(&cpu_hotplug_notifiers, ¬ifier);
cpu_resume(cpu);
}
}
--
1.8.1.4
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [Qemu-devel] [RFC qom-cpu v3 08/10] i386: implement pc interface pc_hot_del_cpu()
2013-09-16 2:40 [Qemu-devel] [RFC qom-cpu v3 00/10] i386: add cpu hot remove support Chen Fan
` (6 preceding siblings ...)
2013-09-16 2:40 ` [Qemu-devel] [RFC qom-cpu v3 07/10] qom cpu: add UNPLUG cpu notifier support Chen Fan
@ 2013-09-16 2:40 ` Chen Fan
2013-09-16 2:40 ` [Qemu-devel] [RFC qom-cpu v3 09/10] piix4: implement function cpu_status_write() for vcpu ejection Chen Fan
` (2 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Chen Fan @ 2013-09-16 2:40 UTC (permalink / raw)
To: qemu-devel; +Cc: Igor Mammedov, Andreas Färber
Implement cpu interface pc_hot_del_cpu() for unrealizing device vCPU.
emiting vcpu-remove notifier to ACPI, then ACPI could send sci interrupt
to OS for hot-remove vcpu.
Signed-off-by: Chen Fan <chen.fan.fnst@cn.fujitsu.com>
---
hw/i386/pc.c | 30 ++++++++++++++++++++++++++++--
qom/cpu.c | 12 ++++++++++++
2 files changed, 40 insertions(+), 2 deletions(-)
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 8ab6e4f..ce7b20f 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -958,8 +958,34 @@ void pc_hot_add_cpu(const int64_t id, Error **errp)
void pc_hot_del_cpu(const int64_t id, Error **errp)
{
- /* TODO: hot remove vCPU. */
- error_setg(errp, "Hot-remove CPU is not supported.");
+ CPUState *cpu;
+ bool found = false;
+ X86CPUClass *xcc;
+
+ CPU_FOREACH(cpu) {
+ CPUClass *cc = CPU_GET_CLASS(cpu);
+ int64_t cpuid = cc->get_arch_id(cpu);
+
+ if (cpuid == id) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ error_setg(errp, "Unable to find cpu-index: %" PRIi64
+ ", it doesn't exist or has been deleted.", id);
+ return;
+ }
+
+ if (cpu == first_cpu && !CPU_NEXT(cpu)) {
+ error_setg(errp, "Unable to delete the last "
+ "one cpu when VM running.");
+ return;
+ }
+
+ xcc = X86_CPU_GET_CLASS(DEVICE(cpu));
+ xcc->parent_unrealize(DEVICE(cpu), errp);
}
void pc_cpus_init(const char *cpu_model, DeviceState *icc_bridge)
diff --git a/qom/cpu.c b/qom/cpu.c
index c6d7ebc..b413a4c 100644
--- a/qom/cpu.c
+++ b/qom/cpu.c
@@ -227,6 +227,17 @@ static void cpu_common_realizefn(DeviceState *dev, Error **errp)
}
}
+static void cpu_common_unrealizefn(DeviceState *dev, Error **errp)
+{
+ CPUNotifier notifier;
+
+ notifier.dev = dev;
+ notifier.type = UNPLUG;
+
+ notifier_list_notify(&cpu_hotplug_notifiers, ¬ifier);
+}
+
+
static void cpu_common_initfn(Object *obj)
{
CPUState *cpu = CPU(obj);
@@ -257,6 +268,7 @@ static void cpu_class_init(ObjectClass *klass, void *data)
k->gdb_read_register = cpu_common_gdb_read_register;
k->gdb_write_register = cpu_common_gdb_write_register;
dc->realize = cpu_common_realizefn;
+ dc->unrealize = cpu_common_unrealizefn;
dc->no_user = 1;
}
--
1.8.1.4
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [Qemu-devel] [RFC qom-cpu v3 09/10] piix4: implement function cpu_status_write() for vcpu ejection
2013-09-16 2:40 [Qemu-devel] [RFC qom-cpu v3 00/10] i386: add cpu hot remove support Chen Fan
` (7 preceding siblings ...)
2013-09-16 2:40 ` [Qemu-devel] [RFC qom-cpu v3 08/10] i386: implement pc interface pc_hot_del_cpu() Chen Fan
@ 2013-09-16 2:40 ` Chen Fan
2013-09-16 2:40 ` [Qemu-devel] [RFC qom-cpu v3 10/10] cpus: reclaim allocated vCPU objects Chen Fan
2013-09-16 7:13 ` [Qemu-devel] [RFC qom-cpu v3 00/10] i386: add cpu hot remove support Andreas Färber
10 siblings, 0 replies; 13+ messages in thread
From: Chen Fan @ 2013-09-16 2:40 UTC (permalink / raw)
To: qemu-devel; +Cc: Igor Mammedov, Andreas Färber
When OS eject a vcpu (like: echo 1 > /sys/bus/acpi/devices/LNXCPUXX/eject),
it will call acpi EJ0 method, the firmware will write the new cpumap, QEMU
will know which vcpu need to be ejected.
Signed-off-by: Chen Fan <chen.fan.fnst@cn.fujitsu.com>
---
hw/acpi/piix4.c | 37 ++++++++++++++++++++++++++++++++++++-
1 file changed, 36 insertions(+), 1 deletion(-)
diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c
index 2ddc9a8..0e9b5bd 100644
--- a/hw/acpi/piix4.c
+++ b/hw/acpi/piix4.c
@@ -61,6 +61,7 @@ struct pci_status {
typedef struct CPUStatus {
uint8_t sts[PIIX4_PROC_LEN];
+ uint8_t old_sts[PIIX4_PROC_LEN];
} CPUStatus;
typedef struct PIIX4PMState {
@@ -610,6 +611,12 @@ static const MemoryRegionOps piix4_pci_ops = {
},
};
+static void acpi_piix_eject_vcpu(int64_t cpuid)
+{
+ /* TODO: eject a vcpu, release allocated vcpu and exit the vcpu pthread. */
+ PIIX4_DPRINTF("vcpu: %" PRIu64 " need to be ejected.\n", cpuid);
+}
+
static uint64_t cpu_status_read(void *opaque, hwaddr addr, unsigned int size)
{
PIIX4PMState *s = opaque;
@@ -622,7 +629,27 @@ static uint64_t cpu_status_read(void *opaque, hwaddr addr, unsigned int size)
static void cpu_status_write(void *opaque, hwaddr addr, uint64_t data,
unsigned int size)
{
- /* TODO: implement VCPU removal on guest signal that CPU can be removed */
+ PIIX4PMState *s = opaque;
+ CPUStatus *cpus = &s->gpe_cpu;
+ uint8_t val;
+ int i;
+ int64_t cpuid = 0;
+
+ val = cpus->old_sts[addr] ^ data;
+
+ if (val == 0) {
+ return;
+ }
+
+ for (i = 0; i < 8; i++) {
+ if (val & 1 << i) {
+ cpuid = 8 * addr + i;
+ }
+ }
+
+ if (cpuid != 0) {
+ acpi_piix_eject_vcpu(cpuid);
+ }
}
static const MemoryRegionOps cpu_hotplug_ops = {
@@ -642,13 +669,20 @@ static void piix4_cpu_hotplug_req(PIIX4PMState *s, CPUState *cpu,
ACPIGPE *gpe = &s->ar.gpe;
CPUClass *k = CPU_GET_CLASS(cpu);
int64_t cpu_id;
+ int i;
assert(s != NULL);
*gpe->sts = *gpe->sts | PIIX4_CPU_HOTPLUG_STATUS;
cpu_id = k->get_arch_id(CPU(cpu));
+
+ for (i = 0; i < PIIX4_PROC_LEN; i++) {
+ g->old_sts[i] = g->sts[i];
+ }
+
if (action == PLUG) {
g->sts[cpu_id / 8] |= (1 << (cpu_id % 8));
+ g->old_sts[cpu_id / 8] |= (1 << (cpu_id % 8));
} else {
g->sts[cpu_id / 8] &= ~(1 << (cpu_id % 8));
}
@@ -687,6 +721,7 @@ static void piix4_acpi_system_hot_add_init(MemoryRegion *parent,
g_assert((id / 8) < PIIX4_PROC_LEN);
s->gpe_cpu.sts[id / 8] |= (1 << (id % 8));
+ s->gpe_cpu.old_sts[id / 8] |= (1 << (id % 8));
}
memory_region_init_io(&s->io_cpu, OBJECT(s), &cpu_hotplug_ops, s,
"acpi-cpu-hotplug", PIIX4_PROC_LEN);
--
1.8.1.4
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [Qemu-devel] [RFC qom-cpu v3 10/10] cpus: reclaim allocated vCPU objects
2013-09-16 2:40 [Qemu-devel] [RFC qom-cpu v3 00/10] i386: add cpu hot remove support Chen Fan
` (8 preceding siblings ...)
2013-09-16 2:40 ` [Qemu-devel] [RFC qom-cpu v3 09/10] piix4: implement function cpu_status_write() for vcpu ejection Chen Fan
@ 2013-09-16 2:40 ` Chen Fan
2013-09-16 7:13 ` [Qemu-devel] [RFC qom-cpu v3 00/10] i386: add cpu hot remove support Andreas Färber
10 siblings, 0 replies; 13+ messages in thread
From: Chen Fan @ 2013-09-16 2:40 UTC (permalink / raw)
To: qemu-devel; +Cc: Igor Mammedov, Andreas Färber
After ACPI get a signal to eject a vCPU, then it will notify
the vCPU thread to exit in KVM, and the vCPU must be removed from CPU list,
before the vCPU really removed, there will release the all related vCPU objects.
Signed-off-by: Chen Fan <chen.fan.fnst@cn.fujitsu.com>
---
cpus.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
hw/acpi/piix4.c | 23 +++++++++++++++++------
include/qom/cpu.h | 10 ++++++++++
include/sysemu/kvm.h | 1 +
kvm-all.c | 25 +++++++++++++++++++++++++
5 files changed, 99 insertions(+), 6 deletions(-)
diff --git a/cpus.c b/cpus.c
index 3bc10f4..c1ad2f4 100644
--- a/cpus.c
+++ b/cpus.c
@@ -714,6 +714,26 @@ void async_run_on_cpu(CPUState *cpu, void (*func)(void *data), void *data)
qemu_cpu_kick(cpu);
}
+static void qemu_kvm_destroy_vcpu(CPUState *cpu)
+{
+ CPU_REMOVE(cpu);
+
+ if (kvm_destroy_vcpu(cpu) < 0) {
+ fprintf(stderr, "kvm_destroy_vcpu failed.\n");
+ exit(1);
+ }
+
+ object_property_set_bool(OBJECT(cpu), false, "realized", NULL);
+ qdev_free(DEVICE(cpu));
+}
+
+static void qemu_tcg_destroy_vcpu(CPUState *cpu)
+{
+ CPU_REMOVE(cpu);
+ object_property_set_bool(OBJECT(cpu), false, "realized", NULL);
+ qdev_free(DEVICE(cpu));
+}
+
static void flush_queued_work(CPUState *cpu)
{
struct qemu_work_item *wi;
@@ -805,6 +825,11 @@ static void *qemu_kvm_cpu_thread_fn(void *arg)
}
}
qemu_kvm_wait_io_event(cpu);
+ if (cpu->exit && !cpu_can_run(cpu)) {
+ qemu_kvm_destroy_vcpu(cpu);
+ qemu_mutex_unlock(&qemu_global_mutex);
+ return NULL;
+ }
}
return NULL;
@@ -857,6 +882,7 @@ static void tcg_exec_all(void);
static void *qemu_tcg_cpu_thread_fn(void *arg)
{
CPUState *cpu = arg;
+ CPUState *remove_cpu = NULL;
qemu_tcg_init_cpu_signals();
qemu_thread_get_self(cpu->thread);
@@ -889,6 +915,16 @@ static void *qemu_tcg_cpu_thread_fn(void *arg)
}
}
qemu_tcg_wait_io_event();
+ CPU_FOREACH(cpu) {
+ if (cpu->exit && !cpu_can_run(cpu)) {
+ remove_cpu = cpu;
+ break;
+ }
+ }
+ if (remove_cpu) {
+ qemu_tcg_destroy_vcpu(remove_cpu);
+ remove_cpu = NULL;
+ }
}
return NULL;
@@ -1045,6 +1081,13 @@ void resume_all_vcpus(void)
}
}
+void cpu_remove(CPUState *cpu)
+{
+ cpu->stop = true;
+ cpu->exit = true;
+ qemu_cpu_kick(cpu);
+}
+
static void qemu_tcg_init_vcpu(CPUState *cpu)
{
/* share a single thread for all cpus with TCG */
@@ -1219,6 +1262,9 @@ static void tcg_exec_all(void)
break;
}
} else if (cpu->stop || cpu->stopped) {
+ if (cpu->exit) {
+ next_cpu = CPU_NEXT(cpu);
+ }
break;
}
}
diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c
index 0e9b5bd..c2cf519 100644
--- a/hw/acpi/piix4.c
+++ b/hw/acpi/piix4.c
@@ -611,10 +611,21 @@ static const MemoryRegionOps piix4_pci_ops = {
},
};
-static void acpi_piix_eject_vcpu(int64_t cpuid)
+static void acpi_piix_eject_vcpu(PIIX4PMState *s, int64_t cpuid)
{
- /* TODO: eject a vcpu, release allocated vcpu and exit the vcpu pthread. */
- PIIX4_DPRINTF("vcpu: %" PRIu64 " need to be ejected.\n", cpuid);
+ CPUStatus *g = &s->gpe_cpu;
+ CPUState *cpu;
+
+ CPU_FOREACH(cpu) {
+ CPUClass *cc = CPU_GET_CLASS(cpu);
+ int64_t id = cc->get_arch_id(cpu);
+
+ if (cpuid == id) {
+ g->old_sts[cpuid / 8] &= ~(1 << (cpuid % 8));
+ cpu_remove(cpu);
+ break;
+ }
+ }
}
static uint64_t cpu_status_read(void *opaque, hwaddr addr, unsigned int size)
@@ -633,7 +644,7 @@ static void cpu_status_write(void *opaque, hwaddr addr, uint64_t data,
CPUStatus *cpus = &s->gpe_cpu;
uint8_t val;
int i;
- int64_t cpuid = 0;
+ int64_t cpuid = -1;
val = cpus->old_sts[addr] ^ data;
@@ -647,8 +658,8 @@ static void cpu_status_write(void *opaque, hwaddr addr, uint64_t data,
}
}
- if (cpuid != 0) {
- acpi_piix_eject_vcpu(cpuid);
+ if (cpuid != -1) {
+ acpi_piix_eject_vcpu(s, cpuid);
}
}
diff --git a/include/qom/cpu.h b/include/qom/cpu.h
index 0238532..eb8d32b 100644
--- a/include/qom/cpu.h
+++ b/include/qom/cpu.h
@@ -181,6 +181,7 @@ struct CPUState {
bool created;
bool stop;
bool stopped;
+ bool exit;
volatile sig_atomic_t exit_request;
volatile sig_atomic_t tcg_exit_req;
uint32_t interrupt_request;
@@ -206,6 +207,7 @@ struct CPUState {
QTAILQ_HEAD(CPUTailQ, CPUState);
extern struct CPUTailQ cpus;
#define CPU_NEXT(cpu) QTAILQ_NEXT(cpu, node)
+#define CPU_REMOVE(cpu) QTAILQ_REMOVE(&cpus, cpu, node)
#define CPU_FOREACH(cpu) QTAILQ_FOREACH(cpu, &cpus, node)
#define CPU_FOREACH_SAFE(cpu, next_cpu) \
QTAILQ_FOREACH_SAFE(cpu, &cpus, node, next_cpu)
@@ -487,6 +489,14 @@ void cpu_exit(CPUState *cpu);
void cpu_resume(CPUState *cpu);
/**
+ * qemu_remove_vcpu:
+ * @cpu: The vCPU to remove.
+ *
+ * Requests the CPU @cpu to be removed.
+ */
+void cpu_remove(CPUState *cpu);
+
+/**
* qemu_init_vcpu:
* @cpu: The vCPU to initialize.
*
diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h
index de74411..fd85605 100644
--- a/include/sysemu/kvm.h
+++ b/include/sysemu/kvm.h
@@ -158,6 +158,7 @@ int kvm_has_intx_set_mask(void);
int kvm_init_vcpu(CPUState *cpu);
int kvm_cpu_exec(CPUState *cpu);
+int kvm_destroy_vcpu(CPUState *cpu);
#ifdef NEED_CPU_H
diff --git a/kvm-all.c b/kvm-all.c
index 52df2a4..dbe56d7 100644
--- a/kvm-all.c
+++ b/kvm-all.c
@@ -225,6 +225,31 @@ static void kvm_reset_vcpu(void *opaque)
kvm_arch_reset_vcpu(cpu);
}
+int kvm_destroy_vcpu(CPUState *cpu)
+{
+ KVMState *s = kvm_state;
+ long mmap_size;
+ int ret = 0;
+
+ DPRINTF("kvm_destroy_vcpu\n");
+
+ mmap_size = kvm_ioctl(s, KVM_GET_VCPU_MMAP_SIZE, 0);
+ if (mmap_size < 0) {
+ ret = mmap_size;
+ DPRINTF("KVM_GET_VCPU_MMAP_SIZE failed\n");
+ goto err;
+ }
+
+ ret = munmap(cpu->kvm_run, mmap_size);
+ if (ret < 0) {
+ goto err;
+ }
+
+ close(cpu->kvm_fd);
+err:
+ return ret;
+}
+
int kvm_init_vcpu(CPUState *cpu)
{
KVMState *s = kvm_state;
--
1.8.1.4
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [RFC qom-cpu v3 00/10] i386: add cpu hot remove support
2013-09-16 2:40 [Qemu-devel] [RFC qom-cpu v3 00/10] i386: add cpu hot remove support Chen Fan
` (9 preceding siblings ...)
2013-09-16 2:40 ` [Qemu-devel] [RFC qom-cpu v3 10/10] cpus: reclaim allocated vCPU objects Chen Fan
@ 2013-09-16 7:13 ` Andreas Färber
2013-09-16 8:11 ` chenfan
10 siblings, 1 reply; 13+ messages in thread
From: Andreas Färber @ 2013-09-16 7:13 UTC (permalink / raw)
To: Chen Fan; +Cc: Igor Mammedov, qemu-devel
Hi,
Am 16.09.2013 04:40, schrieb Chen Fan:
> Via implementing ACPI standard methods _EJ0 in bios, after Guest OS hot remove
> one vCPU, it is able to send a signal to QEMU, then QEMU could notify
> the assigned vCPU of exiting. meanwhile, and intruduce the QOM command 'cpu-del' to remove
> vCPU from QEMU itself.
>
> this work is based on Andreas Färber's qom-cpu branch tree.
> git://github.com/afaerber/qemu-cpu.git
Patch 04/10 is not yet 100% like I think it needs to be wrt unrealize,
but I would like to start cherry-picking APIC preparations from this
series. Since this is still an RFC, permission to do so?
Regards,
Andreas
>
> this series patches must be used with seabios patch and KVM patch together.
>
> for KVM patches:
> http://comments.gmane.org/gmane.comp.emulators.kvm.devel/114347
>
> for seabios patches:
> http://comments.gmane.org/gmane.comp.emulators.qemu/230460
>
> Chen Fan (10):
> x86: move apic_state field from CPUX86State to X86CPU
> apic: remove redundant variable 'apic_no' from apic_init_common()
> apic: remove local_apics array and using CPU_FOREACH instead
> x86: add x86_cpu_unrealizefn() for cpu apic remove
> qmp: add 'cpu-del' command support
> qom cpu: rename variable 'cpu_added_notifier' to
> 'cpu_hotplug_notifier'
> qom cpu: add UNPLUG cpu notifier support
> i386: implement pc interface pc_hot_del_cpu()
> piix4: implement function cpu_status_write() for vcpu ejection
> cpus: reclaim allocated vCPU objects
>
> cpu-exec.c | 2 +-
> cpus.c | 51 ++++++++++++++++++++++++--
> hw/acpi/piix4.c | 66 +++++++++++++++++++++++++++------
> hw/i386/kvm/apic.c | 8 ++++
> hw/i386/kvmvapic.c | 8 ++--
> hw/i386/pc.c | 51 +++++++++++++++++++++-----
> hw/i386/pc_piix.c | 1 +
> hw/intc/apic.c | 81 ++++++++++++++++++++---------------------
> hw/intc/apic_common.c | 6 +--
> include/hw/boards.h | 2 +
> include/hw/i386/apic_internal.h | 2 -
> include/hw/i386/pc.h | 1 +
> include/qom/cpu.h | 20 ++++++++++
> include/sysemu/kvm.h | 1 +
> include/sysemu/sysemu.h | 2 +-
> kvm-all.c | 25 +++++++++++++
> qapi-schema.json | 12 ++++++
> qmp-commands.hx | 23 ++++++++++++
> qmp.c | 9 +++++
> qom/cpu.c | 26 ++++++++++---
> target-i386/cpu-qom.h | 5 +++
> target-i386/cpu.c | 57 +++++++++++++++++++++++------
> target-i386/cpu.h | 4 --
> target-i386/helper.c | 9 ++---
> target-i386/kvm.c | 23 +++++-------
> target-i386/misc_helper.c | 8 ++--
> 26 files changed, 380 insertions(+), 123 deletions(-)
>
--
SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [RFC qom-cpu v3 00/10] i386: add cpu hot remove support
2013-09-16 7:13 ` [Qemu-devel] [RFC qom-cpu v3 00/10] i386: add cpu hot remove support Andreas Färber
@ 2013-09-16 8:11 ` chenfan
0 siblings, 0 replies; 13+ messages in thread
From: chenfan @ 2013-09-16 8:11 UTC (permalink / raw)
To: Andreas Färber; +Cc: Igor Mammedov, qemu-devel
On Mon, 2013-09-16 at 09:13 +0200, Andreas Färber wrote:
> Hi,
>
> Am 16.09.2013 04:40, schrieb Chen Fan:
> > Via implementing ACPI standard methods _EJ0 in bios, after Guest OS hot remove
> > one vCPU, it is able to send a signal to QEMU, then QEMU could notify
> > the assigned vCPU of exiting. meanwhile, and intruduce the QOM command 'cpu-del' to remove
> > vCPU from QEMU itself.
> >
> > this work is based on Andreas Färber's qom-cpu branch tree.
> > git://github.com/afaerber/qemu-cpu.git
>
> Patch 04/10 is not yet 100% like I think it needs to be wrt unrealize,
I just use a simpler way to realize the function unrealize. do you have
any good suggestion?
> but I would like to start cherry-picking APIC preparations from this
> series. Since this is still an RFC, permission to do so?
Yes,of course,This will be very helpful for me.
Thanks.
>
> Regards,
> Andreas
>
> >
> > this series patches must be used with seabios patch and KVM patch together.
> >
> > for KVM patches:
> > http://comments.gmane.org/gmane.comp.emulators.kvm.devel/114347
> >
> > for seabios patches:
> > http://comments.gmane.org/gmane.comp.emulators.qemu/230460
> >
> > Chen Fan (10):
> > x86: move apic_state field from CPUX86State to X86CPU
> > apic: remove redundant variable 'apic_no' from apic_init_common()
> > apic: remove local_apics array and using CPU_FOREACH instead
> > x86: add x86_cpu_unrealizefn() for cpu apic remove
> > qmp: add 'cpu-del' command support
> > qom cpu: rename variable 'cpu_added_notifier' to
> > 'cpu_hotplug_notifier'
> > qom cpu: add UNPLUG cpu notifier support
> > i386: implement pc interface pc_hot_del_cpu()
> > piix4: implement function cpu_status_write() for vcpu ejection
> > cpus: reclaim allocated vCPU objects
> >
> > cpu-exec.c | 2 +-
> > cpus.c | 51 ++++++++++++++++++++++++--
> > hw/acpi/piix4.c | 66 +++++++++++++++++++++++++++------
> > hw/i386/kvm/apic.c | 8 ++++
> > hw/i386/kvmvapic.c | 8 ++--
> > hw/i386/pc.c | 51 +++++++++++++++++++++-----
> > hw/i386/pc_piix.c | 1 +
> > hw/intc/apic.c | 81 ++++++++++++++++++++---------------------
> > hw/intc/apic_common.c | 6 +--
> > include/hw/boards.h | 2 +
> > include/hw/i386/apic_internal.h | 2 -
> > include/hw/i386/pc.h | 1 +
> > include/qom/cpu.h | 20 ++++++++++
> > include/sysemu/kvm.h | 1 +
> > include/sysemu/sysemu.h | 2 +-
> > kvm-all.c | 25 +++++++++++++
> > qapi-schema.json | 12 ++++++
> > qmp-commands.hx | 23 ++++++++++++
> > qmp.c | 9 +++++
> > qom/cpu.c | 26 ++++++++++---
> > target-i386/cpu-qom.h | 5 +++
> > target-i386/cpu.c | 57 +++++++++++++++++++++++------
> > target-i386/cpu.h | 4 --
> > target-i386/helper.c | 9 ++---
> > target-i386/kvm.c | 23 +++++-------
> > target-i386/misc_helper.c | 8 ++--
> > 26 files changed, 380 insertions(+), 123 deletions(-)
> >
>
>
^ permalink raw reply [flat|nested] 13+ messages in thread