SMT-disable enforcement check is moved into a separate
architecture-specific function.
For now this operations only support Arm64. For proper Arm32 support,
there needs to be a mechanism to free per-cpu page tables, allocated in
init_domheap_mappings. Also, hotplug is not supported if ITS enabled,
and partially supported FFA, or TEE is enabled, as they use non-static
IRQ actions.
Remove ifdef guards for x86 in flask, as cpu hotplug is now
supported on more architectures.
Signed-off-by: Mykyta Poturai <mykyta_poturai@epam.com>
---
v6->v7:
* use IS_ENABLED istead of ifdef in more places
* remove unneded variables
* more explicit fallthrough in do_sysctl
v5->v6:
* fix style issues
* rename arch_smt_cpu_disable -> arch_cpu_can_stay_online and invert the
logic
* use IS_ENABLED istead of ifdef
* remove explicit list af arch-specific SYSCTL_CPU_HOTPLUG_* options
from the common handler
* fix flask issue
v4->v5:
* move handling to common code
* rename config to CPU_HOTPUG
* merge with "smp: Move cpu_up/down helpers to common code"
v3->v4:
* don't reimplement cpu_up/down helpers
* add Kconfig option
* fixup formatting
v2->v3:
* no changes
v1->v2:
* remove SMT ops
* remove cpu == 0 checks
* add XSM hooks
* only implement for 64bit Arm
Signed-off-by: Mykyta Poturai <mykyta_poturai@epam.com>
---
xen/arch/arm/smp.c | 9 ++++++++
xen/arch/ppc/stubs.c | 4 ++++
xen/arch/riscv/stubs.c | 5 ++++
xen/arch/x86/include/asm/smp.h | 3 ---
xen/arch/x86/smp.c | 36 +++--------------------------
xen/arch/x86/sysctl.c | 13 ++++-------
xen/common/Kconfig | 6 ++---
xen/common/smp.c | 35 ++++++++++++++++++++++++++++
xen/common/sysctl.c | 42 ++++++++++++++++++++++++++++++++++
xen/include/xen/smp.h | 4 ++++
xen/xsm/flask/hooks.c | 2 --
11 files changed, 109 insertions(+), 50 deletions(-)
diff --git a/xen/arch/arm/smp.c b/xen/arch/arm/smp.c
index b372472188..0ea64d2ee1 100644
--- a/xen/arch/arm/smp.c
+++ b/xen/arch/arm/smp.c
@@ -44,6 +44,15 @@ void smp_send_call_function_mask(const cpumask_t *mask)
}
}
+/*
+ * We currently don't support SMT on ARM so we don't need any special logic for
+ * CPU disabling
+ */
+inline bool arch_cpu_can_stay_online(unsigned int cpu)
+{
+ return true;
+}
+
/*
* Local variables:
* mode: C
diff --git a/xen/arch/ppc/stubs.c b/xen/arch/ppc/stubs.c
index a333f06119..8f280ba080 100644
--- a/xen/arch/ppc/stubs.c
+++ b/xen/arch/ppc/stubs.c
@@ -101,6 +101,10 @@ void smp_send_call_function_mask(const cpumask_t *mask)
BUG_ON("unimplemented");
}
+bool arch_cpu_can_stay_online(unsigned int cpu)
+{
+ BUG_ON("unimplemented");
+}
/* irq.c */
void irq_ack_none(struct irq_desc *desc)
diff --git a/xen/arch/riscv/stubs.c b/xen/arch/riscv/stubs.c
index daadff0138..7c3cda7bc5 100644
--- a/xen/arch/riscv/stubs.c
+++ b/xen/arch/riscv/stubs.c
@@ -70,6 +70,11 @@ void smp_send_call_function_mask(const cpumask_t *mask)
BUG_ON("unimplemented");
}
+bool arch_cpu_can_stay_online(unsigned int cpu)
+{
+ BUG_ON("unimplemented");
+}
+
/* irq.c */
void irq_ack_none(struct irq_desc *desc)
diff --git a/xen/arch/x86/include/asm/smp.h b/xen/arch/x86/include/asm/smp.h
index 3f16e62696..cb3e0fed19 100644
--- a/xen/arch/x86/include/asm/smp.h
+++ b/xen/arch/x86/include/asm/smp.h
@@ -50,9 +50,6 @@ int cpu_add(uint32_t apic_id, uint32_t acpi_id, uint32_t pxm);
void __stop_this_cpu(void);
-long cf_check cpu_up_helper(void *data);
-long cf_check cpu_down_helper(void *data);
-
long cf_check core_parking_helper(void *data);
bool core_parking_remove(unsigned int cpu);
uint32_t get_cur_idle_nums(void);
diff --git a/xen/arch/x86/smp.c b/xen/arch/x86/smp.c
index a49505fb57..b781e933f2 100644
--- a/xen/arch/x86/smp.c
+++ b/xen/arch/x86/smp.c
@@ -418,38 +418,8 @@ void cf_check call_function_interrupt(void)
smp_call_function_interrupt();
}
-#ifdef CONFIG_CPU_HOTPLUG
-long cf_check cpu_up_helper(void *data)
+bool arch_cpu_can_stay_online(unsigned int cpu)
{
- unsigned int cpu = (unsigned long)data;
- int ret = cpu_up(cpu);
-
- /* Have one more go on EBUSY. */
- if ( ret == -EBUSY )
- ret = cpu_up(cpu);
-
- if ( !ret && !opt_smt &&
- cpu_data[cpu].compute_unit_id == INVALID_CUID &&
- cpumask_weight(per_cpu(cpu_sibling_mask, cpu)) > 1 )
- {
- ret = cpu_down_helper(data);
- if ( ret )
- printk("Could not re-offline CPU%u (%d)\n", cpu, ret);
- else
- ret = -EPERM;
- }
-
- return ret;
-}
-
-long cf_check cpu_down_helper(void *data)
-{
- int cpu = (unsigned long)data;
- int ret = cpu_down(cpu);
-
- /* Have one more go on EBUSY. */
- if ( ret == -EBUSY )
- ret = cpu_down(cpu);
- return ret;
+ return opt_smt || cpu_data[cpu].compute_unit_id != INVALID_CUID ||
+ cpumask_weight(per_cpu(cpu_sibling_mask, cpu)) <= 1;
}
-#endif
diff --git a/xen/arch/x86/sysctl.c b/xen/arch/x86/sysctl.c
index bdad44fef1..072726debc 100644
--- a/xen/arch/x86/sysctl.c
+++ b/xen/arch/x86/sysctl.c
@@ -120,7 +120,6 @@ long arch_do_sysctl(
case XEN_SYSCTL_cpu_hotplug:
{
- unsigned int cpu = sysctl->u.cpu_hotplug.cpu;
unsigned int op = sysctl->u.cpu_hotplug.op;
bool plug;
long (*fn)(void *data);
@@ -128,6 +127,7 @@ long arch_do_sysctl(
if ( !IS_ENABLED(CONFIG_CPU_HOTPLUG) )
{
+ ASSERT_UNREACHABLE();
ret = -EOPNOTSUPP;
break;
}
@@ -135,15 +135,10 @@ long arch_do_sysctl(
switch ( op )
{
case XEN_SYSCTL_CPU_HOTPLUG_ONLINE:
- plug = true;
- fn = cpu_up_helper;
- hcpu = _p(cpu);
- break;
-
case XEN_SYSCTL_CPU_HOTPLUG_OFFLINE:
- plug = false;
- fn = cpu_down_helper;
- hcpu = _p(cpu);
+ /* Handled by common code */
+ ASSERT_UNREACHABLE();
+ ret = -EOPNOTSUPP;
break;
case XEN_SYSCTL_CPU_HOTPLUG_SMT_ENABLE:
diff --git a/xen/common/Kconfig b/xen/common/Kconfig
index 0e5b4738a8..9e26217404 100644
--- a/xen/common/Kconfig
+++ b/xen/common/Kconfig
@@ -638,9 +638,9 @@ config SYSTEM_SUSPEND
If unsure, say N.
config CPU_HOTPLUG
- bool "CPU online/offline support"
- depends on X86
- default y
+ bool "CPU online/offline support" if EXPERT || X86
+ depends on X86 || (ARM_64 && !HAS_ITS)
+ default y if X86
help
Enable support for bringing CPUs online and offline at runtime. On
X86 this is required for disabling SMT.
diff --git a/xen/common/smp.c b/xen/common/smp.c
index a011f541f1..e2bf82856e 100644
--- a/xen/common/smp.c
+++ b/xen/common/smp.c
@@ -16,6 +16,7 @@
* GNU General Public License for more details.
*/
+#include <xen/cpu.h>
#include <asm/hardirq.h>
#include <asm/processor.h>
#include <xen/spinlock.h>
@@ -104,6 +105,40 @@ void smp_call_function_interrupt(void)
irq_exit();
}
+#ifdef CONFIG_CPU_HOTPLUG
+long cf_check cpu_up_helper(void *data)
+{
+ unsigned int cpu = (unsigned long)data;
+ int ret = cpu_up(cpu);
+
+ /* Have one more go on EBUSY. */
+ if ( ret == -EBUSY )
+ ret = cpu_up(cpu);
+
+ if ( !ret && !arch_cpu_can_stay_online(cpu) )
+ {
+ ret = cpu_down_helper(data);
+ if ( ret )
+ printk("Could not re-offline CPU%u (%d)\n", cpu, ret);
+ else
+ ret = -EPERM;
+ }
+
+ return ret;
+}
+
+long cf_check cpu_down_helper(void *data)
+{
+ unsigned int cpu = (unsigned long)data;
+ int ret = cpu_down(cpu);
+
+ /* Have one more go on EBUSY. */
+ if ( ret == -EBUSY )
+ ret = cpu_down(cpu);
+ return ret;
+}
+#endif /* CONFIG_CPU_HOTPLUG */
+
/*
* Local variables:
* mode: C
diff --git a/xen/common/sysctl.c b/xen/common/sysctl.c
index 5207664252..da95446039 100644
--- a/xen/common/sysctl.c
+++ b/xen/common/sysctl.c
@@ -483,6 +483,48 @@ long do_sysctl(XEN_GUEST_HANDLE_PARAM(xen_sysctl_t) u_sysctl)
copyback = 1;
break;
+ case XEN_SYSCTL_cpu_hotplug:
+ {
+ unsigned int hp_op = op->u.cpu_hotplug.op;
+ bool plug;
+ long (*fn)(void *data);
+ void *hcpu = _p(op->u.cpu_hotplug.cpu);
+
+ ret = -EOPNOTSUPP;
+ if ( !IS_ENABLED(CONFIG_CPU_HOTPLUG) )
+ break;
+
+ switch ( hp_op )
+ {
+ case XEN_SYSCTL_CPU_HOTPLUG_ONLINE:
+ plug = true;
+ fn = cpu_up_helper;
+ break;
+
+ case XEN_SYSCTL_CPU_HOTPLUG_OFFLINE:
+ plug = false;
+ fn = cpu_down_helper;
+ break;
+
+ default:
+ fn = NULL;
+ break;
+ }
+
+ if ( fn )
+ {
+ ret = plug ? xsm_resource_plug_core(XSM_HOOK)
+ : xsm_resource_unplug_core(XSM_HOOK);
+
+ if ( !ret )
+ ret = continue_hypercall_on_cpu(0, fn, hcpu);
+
+ break;
+ }
+ }
+
+ /* Use the arch handler for cases not handled here */
+ fallthrough;
default:
ret = arch_do_sysctl(op, u_sysctl);
copyback = 0;
diff --git a/xen/include/xen/smp.h b/xen/include/xen/smp.h
index 2ca9ff1bfc..04530738c9 100644
--- a/xen/include/xen/smp.h
+++ b/xen/include/xen/smp.h
@@ -76,4 +76,8 @@ extern void *stack_base[NR_CPUS];
void initialize_cpu_data(unsigned int cpu);
int setup_cpu_root_pgt(unsigned int cpu);
+bool arch_cpu_can_stay_online(unsigned int cpu);
+long cf_check cpu_up_helper(void *data);
+long cf_check cpu_down_helper(void *data);
+
#endif /* __XEN_SMP_H__ */
diff --git a/xen/xsm/flask/hooks.c b/xen/xsm/flask/hooks.c
index b250b27065..01f9d50605 100644
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -835,9 +835,7 @@ static int cf_check flask_sysctl(int cmd)
case XEN_SYSCTL_getdomaininfolist:
case XEN_SYSCTL_page_offline_op:
case XEN_SYSCTL_scheduler_op:
-#ifdef CONFIG_X86
case XEN_SYSCTL_cpu_hotplug:
-#endif
return 0;
case XEN_SYSCTL_tbuf_op:
--
2.51.2
On 30.03.2026 13:59, Mykyta Poturai wrote: > --- a/xen/common/Kconfig > +++ b/xen/common/Kconfig > @@ -638,9 +638,9 @@ config SYSTEM_SUSPEND > If unsure, say N. > > config CPU_HOTPLUG > - bool "CPU online/offline support" > - depends on X86 > - default y > + bool "CPU online/offline support" if EXPERT || X86 Why not just EXPERT? > + depends on X86 || (ARM_64 && !HAS_ITS) The !HAS_ITS is puzzling, and it doesn't help that that option looks misnamed (HAS_* shouldn't have prompts imo). The description says something there, yes, but then also mentions FFA and TEE. Yet for those the option remains available. > + default y if X86 Shorter as "default X86". Jan
On 3/30/26 15:28, Jan Beulich wrote: > On 30.03.2026 13:59, Mykyta Poturai wrote: >> --- a/xen/common/Kconfig >> +++ b/xen/common/Kconfig >> @@ -638,9 +638,9 @@ config SYSTEM_SUSPEND >> If unsure, say N. >> >> config CPU_HOTPLUG >> - bool "CPU online/offline support" >> - depends on X86 >> - default y >> + bool "CPU online/offline support" if EXPERT || X86 > > Why not just EXPERT? Should it be marked as EXPERT on x86? I considered that if the option was non configurable (always enabled), it should stay enabled by default and always visible. >> + depends on X86 || (ARM_64 && !HAS_ITS) > > The !HAS_ITS is puzzling, and it doesn't help that that option looks > misnamed (HAS_* shouldn't have prompts imo). The description says > something there, yes, but then also mentions FFA and TEE. Yet for > those the option remains available. > HAS_ITS can be named better, but this is way out of scope for this series, and for now this is the only way to express this dependency. Regarding TEE and FFA, I removed them from dependencies because hotplug may work with some TEE OS configurations, but not with all of them. That is the main reason it is marked as EXPERT for Arm64. >> + default y if X86 > > Shorter as "default X86". > > Jan Got it. -- Mykyta
On 09.04.2026 16:34, Mykyta Poturai wrote: > On 3/30/26 15:28, Jan Beulich wrote: >> On 30.03.2026 13:59, Mykyta Poturai wrote: >>> --- a/xen/common/Kconfig >>> +++ b/xen/common/Kconfig >>> @@ -638,9 +638,9 @@ config SYSTEM_SUSPEND >>> If unsure, say N. >>> >>> config CPU_HOTPLUG >>> - bool "CPU online/offline support" >>> - depends on X86 >>> - default y >>> + bool "CPU online/offline support" if EXPERT || X86 >> >> Why not just EXPERT? > > Should it be marked as EXPERT on x86? I considered that if the option > was non configurable (always enabled), it should stay enabled by default > and always visible. As you say, it wasn't configurable before. Imo that's a good sign that, at least for starters, we should limit its disabling to experts. Jan
© 2016 - 2026 Red Hat, Inc.