xen/arm: Disable timers and release their interrupts on CPU hot-unplug
authorMirela Simonovic <mirela.simonovic@aggios.com>
Fri, 1 Jun 2018 13:17:48 +0000 (15:17 +0200)
committerJulien Grall <julien.grall@arm.com>
Tue, 5 Jun 2018 18:08:05 +0000 (19:08 +0100)
When a CPU is hot-unplugged we need to disable timers and release
their interrupts in order to free the memory that was allocated when
interrupts were requested (using request_irq()). The request_irq()
is called for each timer interrupt when the CPU gets hotplugged
(start_secondary->init_timer_interrupt->request_irq).
With this patch timers will be disabled and interrupts will be
released when the newly added callback receives CPU_DYING event.

Signed-off-by: Mirela Simonovic <mirela.simonovic@aggios.com>
Acked-by: Julien Grall <julien.grall@arm.com>
xen/arch/arm/time.c

index c11fcfeadd0af714d0dccc7ad9ee3227b9656836..1635c8822d1926015f7c29ff26f60c36b6a98317 100644 (file)
@@ -29,6 +29,8 @@
 #include <xen/sched.h>
 #include <xen/event.h>
 #include <xen/acpi.h>
+#include <xen/cpu.h>
+#include <xen/notifier.h>
 #include <asm/system.h>
 #include <asm/time.h>
 #include <asm/vgic.h>
@@ -312,6 +314,21 @@ void init_timer_interrupt(void)
     check_timer_irq_cfg(timer_irq[TIMER_PHYS_NONSECURE_PPI], "NS-physical");
 }
 
+/*
+ * Revert actions done in init_timer_interrupt that are required to properly
+ * disable this CPU.
+ */
+static void deinit_timer_interrupt(void)
+{
+    WRITE_SYSREG32(0, CNTP_CTL_EL0);    /* Disable physical timer */
+    WRITE_SYSREG32(0, CNTHP_CTL_EL2);   /* Disable hypervisor's timer */
+    isb();
+
+    release_irq(timer_irq[TIMER_HYP_PPI], NULL);
+    release_irq(timer_irq[TIMER_VIRT_PPI], NULL);
+    release_irq(timer_irq[TIMER_PHYS_NONSECURE_PPI], NULL);
+}
+
 /* Wait a set number of microseconds */
 void udelay(unsigned long usecs)
 {
@@ -340,6 +357,34 @@ void domain_set_time_offset(struct domain *d, int64_t time_offset_seconds)
     /* XXX update guest visible wallclock time */
 }
 
+static int cpu_time_callback(struct notifier_block *nfb,
+                             unsigned long action,
+                             void *hcpu)
+{
+    switch ( action )
+    {
+    case CPU_DYING:
+        deinit_timer_interrupt();
+        break;
+    default:
+        break;
+    }
+
+    return NOTIFY_DONE;
+}
+
+static struct notifier_block cpu_time_nfb = {
+    .notifier_call = cpu_time_callback,
+};
+
+static int __init cpu_time_notifier_init(void)
+{
+    register_cpu_notifier(&cpu_time_nfb);
+
+    return 0;
+}
+__initcall(cpu_time_notifier_init);
+
 /*
  * Local variables:
  * mode: C