# HG changeset patch # User Juergen Gross # Date 1332404504 -3600 # Node ID 12d8bb0e02edf68b4a0526a2989a660218535fcb # Parent 4e1d091d10d83130842170cd61f1194e5459f2aa Allow ACPI state change with active cpupools Changing the ACPI state (e.g. power off) while not all cpus are in cpupool 0 will currently crash the hypervisor during disabling the other cpus. Up to now only cpus in Pool-0 were allowed to go offline. There is no reason why a cpu in another cpupool can't be taken offline if there are still other cpus available in that cpupool. As disabling the cpus for an ACPI state change is only a temporary measure (if not poweroff, of course) and all domains are paused in this case, there is no reason to reject removing the last cpu from a cpupool if only paused domains are in this cpupool. The cpus taken offline from a cpupool will be still associated with that cpupool to enable adding them automatically when they are becoming online again. Signed-off-by: Juergen Gross diff -r 4e1d091d10d8 -r 12d8bb0e02ed xen/arch/x86/smpboot.c --- a/xen/arch/x86/smpboot.c Fri Mar 16 15:24:25 2012 +0000 +++ b/xen/arch/x86/smpboot.c Thu Mar 22 09:21:44 2012 +0100 @@ -863,7 +863,7 @@ void __cpu_disable(void) remove_siblinginfo(cpu); /* It's now safe to remove this processor from the online map */ - cpumask_clear_cpu(cpu, cpupool0->cpu_valid); + cpupool_disable_cpu(cpu); cpumask_clear_cpu(cpu, &cpu_online_map); fixup_irqs(); diff -r 4e1d091d10d8 -r 12d8bb0e02ed xen/common/cpupool.c --- a/xen/common/cpupool.c Fri Mar 16 15:24:25 2012 +0000 +++ b/xen/common/cpupool.c Thu Mar 22 09:21:44 2012 +0100 @@ -24,6 +24,7 @@ struct cpupool *cpupool0; /* Initial cpupool with Dom0 */ cpumask_t cpupool_free_cpus; /* cpus not in any cpupool */ +static cpumask_t cpupool_free_offline_cpus; static struct cpupool *cpupool_list; /* linked list, sorted by poolid */ @@ -41,16 +42,25 @@ static struct cpupool *alloc_cpupool_str { struct cpupool *c = xzalloc(struct cpupool); - if ( c && zalloc_cpumask_var(&c->cpu_valid) ) + if ( !c ) + return NULL; + if ( !zalloc_cpumask_var(&c->cpu_valid) ) + goto fail; + if ( zalloc_cpumask_var(&c->cpu_offline) ) return c; + + free_cpumask_var(c->cpu_valid); +fail: xfree(c); return NULL; } static void free_cpupool_struct(struct cpupool *c) { - if ( c ) - free_cpumask_var(c->cpu_valid); + if ( !c ) + return; + free_cpumask_var(c->cpu_valid); + free_cpumask_var(c->cpu_offline); xfree(c); } @@ -413,30 +423,67 @@ void cpupool_rm_domain(struct domain *d) /* * called to add a new cpu to pool admin + * a temporarily disabled cpu will be added to its former pool * we add a hotplugged cpu to the cpupool0 to be able to add it to dom0 */ static void cpupool_cpu_add(unsigned int cpu) { + struct cpupool **c; + spin_lock(&cpupool_lock); cpumask_clear_cpu(cpu, &cpupool_locked_cpus); cpumask_set_cpu(cpu, &cpupool_free_cpus); + if ( cpumask_test_cpu(cpu, &cpupool_free_offline_cpus) ) + { + cpumask_clear_cpu(cpu, &cpupool_free_offline_cpus); + goto out; + } + for_each_cpupool(c) + { + if ( cpumask_test_cpu(cpu, (*c)->cpu_offline) ) + { + cpumask_clear_cpu(cpu, (*c)->cpu_offline); + cpupool_assign_cpu_locked(*c, cpu); + goto out; + } + } cpupool_assign_cpu_locked(cpupool0, cpu); +out: spin_unlock(&cpupool_lock); } /* * called to remove a cpu from pool admin - * the cpu to be removed is locked to avoid removing it from dom0 + * the cpu to be removed is locked to avoid removing it from any cpupool + * (the real remove at end of taking a cpu down must not take the cpupool + * lock) * returns failure if not in pool0 */ static int cpupool_cpu_remove(unsigned int cpu) { int ret = 0; - + struct cpupool *c; + struct domain *d; + spin_lock(&cpupool_lock); - if ( !cpumask_test_cpu(cpu, cpupool0->cpu_valid)) - ret = -EBUSY; - else + c = per_cpu(cpupool, cpu); + if ( c == NULL ) + goto out; + if ( cpumask_test_cpu(cpu, c->cpu_valid) ) + { + if ( cpumask_weight(c->cpu_valid) > 1 ) + goto out; + rcu_read_lock(&domlist_read_lock); + for_each_domain_in_cpupool(d, c) + { + if ( !d->is_dying && !atomic_read(&d->pause_count) ) + ret = -EBUSY; + } + rcu_read_unlock(&domlist_read_lock); + } + +out: + if ( !ret ) cpumask_set_cpu(cpu, &cpupool_locked_cpus); spin_unlock(&cpupool_lock); @@ -597,6 +644,28 @@ int cpupool_do_sysctl(struct xen_sysctl_ return ret; } +/* + * remove a cpu from configuration + * called at end of cpu offlining + * we must not take the cpupool lock + */ +void cpupool_disable_cpu(unsigned int cpu) +{ + struct cpupool *c; + + c = per_cpu(cpupool, cpu); + if ( c == NULL ) + { + cpumask_clear_cpu(cpu, &cpupool_free_cpus); + cpumask_set_cpu(cpu, &cpupool_free_offline_cpus); + } + else if ( cpumask_test_cpu(cpu, c->cpu_valid) ) + { + cpumask_clear_cpu(cpu, c->cpu_valid); + cpumask_set_cpu(cpu, c->cpu_offline); + } +} + void dump_runq(unsigned char key) { unsigned long flags; diff -r 4e1d091d10d8 -r 12d8bb0e02ed xen/include/xen/sched-if.h --- a/xen/include/xen/sched-if.h Fri Mar 16 15:24:25 2012 +0000 +++ b/xen/include/xen/sched-if.h Thu Mar 22 09:21:44 2012 +0100 @@ -199,6 +199,7 @@ struct cpupool { int cpupool_id; cpumask_var_t cpu_valid; /* all cpus assigned to pool */ + cpumask_var_t cpu_offline; /* cpus from pool taken offline */ struct cpupool *next; unsigned int n_dom; struct scheduler *sched; diff -r 4e1d091d10d8 -r 12d8bb0e02ed xen/include/xen/sched.h --- a/xen/include/xen/sched.h Fri Mar 16 15:24:25 2012 +0000 +++ b/xen/include/xen/sched.h Thu Mar 22 09:21:44 2012 +0100 @@ -715,6 +715,7 @@ int cpupool_do_sysctl(struct xen_sysctl_ int cpupool_do_sysctl(struct xen_sysctl_cpupool_op *op); void schedule_dump(struct cpupool *c); extern void dump_runq(unsigned char key); +void cpupool_disable_cpu(unsigned int cpu); #define num_cpupool_cpus(c) cpumask_weight((c)->cpu_valid)