arm/xen: Initialize event channels earlier

Message ID 1390920842-21886-1-git-send-email-julien.grall@linaro.org
State Superseded
Headers show

Commit Message

Julien Grall Jan. 28, 2014, 2:54 p.m.
Event channels driver needs to be initialized very early. Until now, Xen
initialization was done after all CPUs was bring up.

We can safely move the initialization to an early initcall.

Also use a cpu notifier to:
    - Register the VCPU when the CPU is prepared
    - Enable event channel IRQ when the CPU is running

Signed-off-by: Julien Grall <julien.grall@linaro.org>
---
 arch/arm/xen/enlighten.c |   84 ++++++++++++++++++++++++++++++----------------
 1 file changed, 55 insertions(+), 29 deletions(-)

Comments

Stefano Stabellini Jan. 28, 2014, 5:13 p.m. | #1
On Tue, 28 Jan 2014, Julien Grall wrote:
> Event channels driver needs to be initialized very early. Until now, Xen
> initialization was done after all CPUs was bring up.
> 
> We can safely move the initialization to an early initcall.
> 
> Also use a cpu notifier to:
>     - Register the VCPU when the CPU is prepared
>     - Enable event channel IRQ when the CPU is running
> 
> Signed-off-by: Julien Grall <julien.grall@linaro.org>

Did you test this patch in Dom0 as well as in DomUs?


>  arch/arm/xen/enlighten.c |   84 ++++++++++++++++++++++++++++++----------------
>  1 file changed, 55 insertions(+), 29 deletions(-)
> 
> diff --git a/arch/arm/xen/enlighten.c b/arch/arm/xen/enlighten.c
> index 293eeea..39b668e 100644
> --- a/arch/arm/xen/enlighten.c
> +++ b/arch/arm/xen/enlighten.c
> @@ -23,6 +23,7 @@
>  #include <linux/of_address.h>
>  #include <linux/cpuidle.h>
>  #include <linux/cpufreq.h>
> +#include <linux/cpu.h>
>  
>  #include <linux/mm.h>
>  
> @@ -154,12 +155,11 @@ int xen_unmap_domain_mfn_range(struct vm_area_struct *vma,
>  }
>  EXPORT_SYMBOL_GPL(xen_unmap_domain_mfn_range);
>  
> -static void __init xen_percpu_init(void *unused)
> +static void xen_percpu_init(int cpu)
>  {
>  	struct vcpu_register_vcpu_info info;
>  	struct vcpu_info *vcpup;
>  	int err;
> -	int cpu = get_cpu();
>  
>  	pr_info("Xen: initializing cpu%d\n", cpu);
>  	vcpup = per_cpu_ptr(xen_vcpu_info, cpu);
> @@ -170,9 +170,11 @@ static void __init xen_percpu_init(void *unused)
>  	err = HYPERVISOR_vcpu_op(VCPUOP_register_vcpu_info, cpu, &info);
>  	BUG_ON(err);
>  	per_cpu(xen_vcpu, cpu) = vcpup;
> +}
>  
> +static void xen_interrupt_init(void)
> +{
>  	enable_percpu_irq(xen_events_irq, 0);
> -	put_cpu();
>  }
>  
>  static void xen_restart(enum reboot_mode reboot_mode, const char *cmd)
> @@ -193,6 +195,36 @@ static void xen_power_off(void)
>  		BUG();
>  }
>  
> +static irqreturn_t xen_arm_callback(int irq, void *arg)
> +{
> +	xen_hvm_evtchn_do_upcall();
> +	return IRQ_HANDLED;
> +}
> +
> +static int xen_cpu_notification(struct notifier_block *self,
> +				unsigned long action,
> +				void *hcpu)
> +{
> +	int cpu = (long)hcpu;
> +
> +	switch (action) {
> +	case CPU_UP_PREPARE:
> +		xen_percpu_init(cpu);
> +		break;
> +	case CPU_STARTING:
> +		xen_interrupt_init();
> +		break;

Is CPU_STARTING guaranteed to be called on the new cpu only?
If so, why not call both xen_percpu_init and xen_interrupt_init on
CPU_STARTING?
As it stands I think you introduced a subtle change (that might be OK
but I think is unintentional): xen_percpu_init might not be called from
the same cpu as its target anymore.


> +	default:
> +		break;
> +	}
> +
> +	return NOTIFY_OK;
> +}
> +
> +static struct notifier_block xen_cpu_notifier = {
> +	.notifier_call = xen_cpu_notification,
> +};
> +
>  /*
>   * see Documentation/devicetree/bindings/arm/xen.txt for the
>   * documentation of the Xen Device Tree format.
> @@ -209,6 +241,7 @@ static int __init xen_guest_init(void)
>  	const char *xen_prefix = "xen,xen-";
>  	struct resource res;
>  	phys_addr_t grant_frames;
> +	int cpu;
>  
>  	node = of_find_compatible_node(NULL, NULL, "xen,xen");
>  	if (!node) {
> @@ -281,9 +314,27 @@ static int __init xen_guest_init(void)
>  	disable_cpuidle();
>  	disable_cpufreq();
>  
> +	xen_init_IRQ();
> +
> +	if (xen_events_irq < 0)
> +		return -ENODEV;

Since you are moving this code to xen_guest_init, you can check for
xen_events_irq earlier on, where we parse the irq from device tree.


> +	if (request_percpu_irq(xen_events_irq, xen_arm_callback,
> +			       "events", &xen_vcpu)) {
> +		pr_err("Error request IRQ %d\n", xen_events_irq);
> +		return -EINVAL;
> +	}
> +
> +	cpu = get_cpu();
> +	xen_percpu_init(cpu);
> +	xen_interrupt_init();
> +	put_cpu();
> +
> +	register_cpu_notifier(&xen_cpu_notifier);
> +
>  	return 0;
>  }
> -core_initcall(xen_guest_init);
> +early_initcall(xen_guest_init);
>  
>  static int __init xen_pm_init(void)
>  {
> @@ -297,31 +348,6 @@ static int __init xen_pm_init(void)
>  }
>  late_initcall(xen_pm_init);
>  
> -static irqreturn_t xen_arm_callback(int irq, void *arg)
> -{
> -	xen_hvm_evtchn_do_upcall();
> -	return IRQ_HANDLED;
> -}
> -
> -static int __init xen_init_events(void)
> -{
> -	if (!xen_domain() || xen_events_irq < 0)
> -		return -ENODEV;
> -
> -	xen_init_IRQ();
> -
> -	if (request_percpu_irq(xen_events_irq, xen_arm_callback,
> -			"events", &xen_vcpu)) {
> -		pr_err("Error requesting IRQ %d\n", xen_events_irq);
> -		return -EINVAL;
> -	}
> -
> -	on_each_cpu(xen_percpu_init, NULL, 0);
> -
> -	return 0;
> -}
> -postcore_initcall(xen_init_events);
> -
>  /* In the hypervisor.S file. */
>  EXPORT_SYMBOL_GPL(HYPERVISOR_event_channel_op);
>  EXPORT_SYMBOL_GPL(HYPERVISOR_grant_table_op);
> -- 
> 1.7.10.4
>
Julien Grall Jan. 28, 2014, 5:35 p.m. | #2
On 01/28/2014 05:13 PM, Stefano Stabellini wrote:
> On Tue, 28 Jan 2014, Julien Grall wrote:
>> Event channels driver needs to be initialized very early. Until now, Xen
>> initialization was done after all CPUs was bring up.
>>
>> We can safely move the initialization to an early initcall.
>>
>> Also use a cpu notifier to:
>>     - Register the VCPU when the CPU is prepared
>>     - Enable event channel IRQ when the CPU is running
>>
>> Signed-off-by: Julien Grall <julien.grall@linaro.org>
> 
> Did you test this patch in Dom0 as well as in DomUs?
> 

Only try dom0. I will try domU.

> 
>>  arch/arm/xen/enlighten.c |   84 ++++++++++++++++++++++++++++++----------------
>>  1 file changed, 55 insertions(+), 29 deletions(-)
>>
>> diff --git a/arch/arm/xen/enlighten.c b/arch/arm/xen/enlighten.c
>> index 293eeea..39b668e 100644
>> --- a/arch/arm/xen/enlighten.c
>> +++ b/arch/arm/xen/enlighten.c
>> @@ -23,6 +23,7 @@
>>  #include <linux/of_address.h>
>>  #include <linux/cpuidle.h>
>>  #include <linux/cpufreq.h>
>> +#include <linux/cpu.h>
>>  
>>  #include <linux/mm.h>
>>  
>> @@ -154,12 +155,11 @@ int xen_unmap_domain_mfn_range(struct vm_area_struct *vma,
>>  }
>>  EXPORT_SYMBOL_GPL(xen_unmap_domain_mfn_range);
>>  
>> -static void __init xen_percpu_init(void *unused)
>> +static void xen_percpu_init(int cpu)
>>  {
>>  	struct vcpu_register_vcpu_info info;
>>  	struct vcpu_info *vcpup;
>>  	int err;
>> -	int cpu = get_cpu();
>>  
>>  	pr_info("Xen: initializing cpu%d\n", cpu);
>>  	vcpup = per_cpu_ptr(xen_vcpu_info, cpu);
>> @@ -170,9 +170,11 @@ static void __init xen_percpu_init(void *unused)
>>  	err = HYPERVISOR_vcpu_op(VCPUOP_register_vcpu_info, cpu, &info);
>>  	BUG_ON(err);
>>  	per_cpu(xen_vcpu, cpu) = vcpup;
>> +}
>>  
>> +static void xen_interrupt_init(void)
>> +{
>>  	enable_percpu_irq(xen_events_irq, 0);
>> -	put_cpu();
>>  }
>>  
>>  static void xen_restart(enum reboot_mode reboot_mode, const char *cmd)
>> @@ -193,6 +195,36 @@ static void xen_power_off(void)
>>  		BUG();
>>  }
>>  
>> +static irqreturn_t xen_arm_callback(int irq, void *arg)
>> +{
>> +	xen_hvm_evtchn_do_upcall();
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static int xen_cpu_notification(struct notifier_block *self,
>> +				unsigned long action,
>> +				void *hcpu)
>> +{
>> +	int cpu = (long)hcpu;
>> +
>> +	switch (action) {
>> +	case CPU_UP_PREPARE:
>> +		xen_percpu_init(cpu);
>> +		break;
>> +	case CPU_STARTING:
>> +		xen_interrupt_init();
>> +		break;
> 
> Is CPU_STARTING guaranteed to be called on the new cpu only?

Yes.

> If so, why not call both xen_percpu_init and xen_interrupt_init on
> CPU_STARTING?

Just in case that xen_vcpu is used somewhere else by a cpu notifier
callback CPU_STARTING. We don't know which callback is called first.

> As it stands I think you introduced a subtle change (that might be OK
> but I think is unintentional): xen_percpu_init might not be called from
> the same cpu as its target anymore.

No, xen_percpu_init and xen_interrupt_init are called on the boot cpu at
the end of xen_guest_init.

> 
> 
>> +	default:
>> +		break;
>> +	}
>> +
>> +	return NOTIFY_OK;
>> +}
>> +
>> +static struct notifier_block xen_cpu_notifier = {
>> +	.notifier_call = xen_cpu_notification,
>> +};
>> +
>>  /*
>>   * see Documentation/devicetree/bindings/arm/xen.txt for the
>>   * documentation of the Xen Device Tree format.
>> @@ -209,6 +241,7 @@ static int __init xen_guest_init(void)
>>  	const char *xen_prefix = "xen,xen-";
>>  	struct resource res;
>>  	phys_addr_t grant_frames;
>> +	int cpu;
>>  
>>  	node = of_find_compatible_node(NULL, NULL, "xen,xen");
>>  	if (!node) {
>> @@ -281,9 +314,27 @@ static int __init xen_guest_init(void)
>>  	disable_cpuidle();
>>  	disable_cpufreq();
>>  
>> +	xen_init_IRQ();
>> +
>> +	if (xen_events_irq < 0)
>> +		return -ENODEV;
> 
> Since you are moving this code to xen_guest_init, you can check for
> xen_events_irq earlier on, where we parse the irq from device tree.

Will do.
Stefano Stabellini Jan. 28, 2014, 5:46 p.m. | #3
On Tue, 28 Jan 2014, Julien Grall wrote:
> >> +static int xen_cpu_notification(struct notifier_block *self,
> >> +				unsigned long action,
> >> +				void *hcpu)
> >> +{
> >> +	int cpu = (long)hcpu;
> >> +
> >> +	switch (action) {
> >> +	case CPU_UP_PREPARE:
> >> +		xen_percpu_init(cpu);
> >> +		break;
> >> +	case CPU_STARTING:
> >> +		xen_interrupt_init();
> >> +		break;
> > 
> > Is CPU_STARTING guaranteed to be called on the new cpu only?
> 
> Yes.
> 
> > If so, why not call both xen_percpu_init and xen_interrupt_init on
> > CPU_STARTING?
> 
> Just in case that xen_vcpu is used somewhere else by a cpu notifier
> callback CPU_STARTING. We don't know which callback is called first.

Could you please elaborate a bit more on the problem you are trying to
describe?


> > As it stands I think you introduced a subtle change (that might be OK
> > but I think is unintentional): xen_percpu_init might not be called from
> > the same cpu as its target anymore.
> 
> No, xen_percpu_init and xen_interrupt_init are called on the boot cpu at
> the end of xen_guest_init.
 
Is CPU_UP_PREPARE guaranteed to be called on the target cpu? I think
not, therefore you would be executing xen_percpu_init for cpu1 on cpu0.
Julien Grall Jan. 28, 2014, 6 p.m. | #4
On 01/28/2014 05:46 PM, Stefano Stabellini wrote:
> On Tue, 28 Jan 2014, Julien Grall wrote:
>>>> +static int xen_cpu_notification(struct notifier_block *self,
>>>> +				unsigned long action,
>>>> +				void *hcpu)
>>>> +{
>>>> +	int cpu = (long)hcpu;
>>>> +
>>>> +	switch (action) {
>>>> +	case CPU_UP_PREPARE:
>>>> +		xen_percpu_init(cpu);
>>>> +		break;
>>>> +	case CPU_STARTING:
>>>> +		xen_interrupt_init();
>>>> +		break;
>>>
>>> Is CPU_STARTING guaranteed to be called on the new cpu only?
>>
>> Yes.
>>
>>> If so, why not call both xen_percpu_init and xen_interrupt_init on
>>> CPU_STARTING?
>>
>> Just in case that xen_vcpu is used somewhere else by a cpu notifier
>> callback CPU_STARTING. We don't know which callback is called first.
> 
> Could you please elaborate a bit more on the problem you are trying to
> describe?

We want to make sure that the vcpu is registered correctly. If not, we
can't skip it and avoid xen to have a "dead" VCPU to schedule due to BUG_ON.

I agree that now we have a BUG_ON in the middle of xen_percpu_init, but
it's possible to return an error. In this case Linux will skip this cpu
and continue to boot.

>>> As it stands I think you introduced a subtle change (that might be OK
>>> but I think is unintentional): xen_percpu_init might not be called from
>>> the same cpu as its target anymore.
>>
>> No, xen_percpu_init and xen_interrupt_init are called on the boot cpu at
>> the end of xen_guest_init.
>  
> Is CPU_UP_PREPARE guaranteed to be called on the target cpu? I think
> not, therefore you would be executing xen_percpu_init for cpu1 on cpu0.
>

I don't see any issue to execute xen_percpu_init for cpu1 on cpu0, all
the code is taking directly the vcpu ID to initialize.
Stefano Stabellini Jan. 28, 2014, 6:11 p.m. | #5
On Tue, 28 Jan 2014, Julien Grall wrote:
> On 01/28/2014 05:46 PM, Stefano Stabellini wrote:
> > On Tue, 28 Jan 2014, Julien Grall wrote:
> >>>> +static int xen_cpu_notification(struct notifier_block *self,
> >>>> +				unsigned long action,
> >>>> +				void *hcpu)
> >>>> +{
> >>>> +	int cpu = (long)hcpu;
> >>>> +
> >>>> +	switch (action) {
> >>>> +	case CPU_UP_PREPARE:
> >>>> +		xen_percpu_init(cpu);
> >>>> +		break;
> >>>> +	case CPU_STARTING:
> >>>> +		xen_interrupt_init();
> >>>> +		break;
> >>>
> >>> Is CPU_STARTING guaranteed to be called on the new cpu only?
> >>
> >> Yes.
> >>
> >>> If so, why not call both xen_percpu_init and xen_interrupt_init on
> >>> CPU_STARTING?
> >>
> >> Just in case that xen_vcpu is used somewhere else by a cpu notifier
> >> callback CPU_STARTING. We don't know which callback is called first.
> > 
> > Could you please elaborate a bit more on the problem you are trying to
> > describe?
> 
> We want to make sure that the vcpu is registered correctly. If not, we
> can't skip it and avoid xen to have a "dead" VCPU to schedule due to BUG_ON.
> 
> I agree that now we have a BUG_ON in the middle of xen_percpu_init, but
> it's possible to return an error. In this case Linux will skip this cpu
> and continue to boot.

I think there are no benefits in having two separate functions
(xen_percpu_init and xen_interrupt_init) called at two different points
(CPU_UP_PREPARE and CPU_STARTING).
I would simply have one. Simpler is better.


> >>> As it stands I think you introduced a subtle change (that might be OK
> >>> but I think is unintentional): xen_percpu_init might not be called from
> >>> the same cpu as its target anymore.
> >>
> >> No, xen_percpu_init and xen_interrupt_init are called on the boot cpu at
> >> the end of xen_guest_init.
> >  
> > Is CPU_UP_PREPARE guaranteed to be called on the target cpu? I think
> > not, therefore you would be executing xen_percpu_init for cpu1 on cpu0.
> >
> 
> I don't see any issue to execute xen_percpu_init for cpu1 on cpu0, all
> the code is taking directly the vcpu ID to initialize.

Me neither, I was simply pointing out that you made this change without
writing it in the commit message (therefore I assume it might be
unintended).

Patch

diff --git a/arch/arm/xen/enlighten.c b/arch/arm/xen/enlighten.c
index 293eeea..39b668e 100644
--- a/arch/arm/xen/enlighten.c
+++ b/arch/arm/xen/enlighten.c
@@ -23,6 +23,7 @@ 
 #include <linux/of_address.h>
 #include <linux/cpuidle.h>
 #include <linux/cpufreq.h>
+#include <linux/cpu.h>
 
 #include <linux/mm.h>
 
@@ -154,12 +155,11 @@  int xen_unmap_domain_mfn_range(struct vm_area_struct *vma,
 }
 EXPORT_SYMBOL_GPL(xen_unmap_domain_mfn_range);
 
-static void __init xen_percpu_init(void *unused)
+static void xen_percpu_init(int cpu)
 {
 	struct vcpu_register_vcpu_info info;
 	struct vcpu_info *vcpup;
 	int err;
-	int cpu = get_cpu();
 
 	pr_info("Xen: initializing cpu%d\n", cpu);
 	vcpup = per_cpu_ptr(xen_vcpu_info, cpu);
@@ -170,9 +170,11 @@  static void __init xen_percpu_init(void *unused)
 	err = HYPERVISOR_vcpu_op(VCPUOP_register_vcpu_info, cpu, &info);
 	BUG_ON(err);
 	per_cpu(xen_vcpu, cpu) = vcpup;
+}
 
+static void xen_interrupt_init(void)
+{
 	enable_percpu_irq(xen_events_irq, 0);
-	put_cpu();
 }
 
 static void xen_restart(enum reboot_mode reboot_mode, const char *cmd)
@@ -193,6 +195,36 @@  static void xen_power_off(void)
 		BUG();
 }
 
+static irqreturn_t xen_arm_callback(int irq, void *arg)
+{
+	xen_hvm_evtchn_do_upcall();
+	return IRQ_HANDLED;
+}
+
+static int xen_cpu_notification(struct notifier_block *self,
+				unsigned long action,
+				void *hcpu)
+{
+	int cpu = (long)hcpu;
+
+	switch (action) {
+	case CPU_UP_PREPARE:
+		xen_percpu_init(cpu);
+		break;
+	case CPU_STARTING:
+		xen_interrupt_init();
+		break;
+	default:
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block xen_cpu_notifier = {
+	.notifier_call = xen_cpu_notification,
+};
+
 /*
  * see Documentation/devicetree/bindings/arm/xen.txt for the
  * documentation of the Xen Device Tree format.
@@ -209,6 +241,7 @@  static int __init xen_guest_init(void)
 	const char *xen_prefix = "xen,xen-";
 	struct resource res;
 	phys_addr_t grant_frames;
+	int cpu;
 
 	node = of_find_compatible_node(NULL, NULL, "xen,xen");
 	if (!node) {
@@ -281,9 +314,27 @@  static int __init xen_guest_init(void)
 	disable_cpuidle();
 	disable_cpufreq();
 
+	xen_init_IRQ();
+
+	if (xen_events_irq < 0)
+		return -ENODEV;
+
+	if (request_percpu_irq(xen_events_irq, xen_arm_callback,
+			       "events", &xen_vcpu)) {
+		pr_err("Error request IRQ %d\n", xen_events_irq);
+		return -EINVAL;
+	}
+
+	cpu = get_cpu();
+	xen_percpu_init(cpu);
+	xen_interrupt_init();
+	put_cpu();
+
+	register_cpu_notifier(&xen_cpu_notifier);
+
 	return 0;
 }
-core_initcall(xen_guest_init);
+early_initcall(xen_guest_init);
 
 static int __init xen_pm_init(void)
 {
@@ -297,31 +348,6 @@  static int __init xen_pm_init(void)
 }
 late_initcall(xen_pm_init);
 
-static irqreturn_t xen_arm_callback(int irq, void *arg)
-{
-	xen_hvm_evtchn_do_upcall();
-	return IRQ_HANDLED;
-}
-
-static int __init xen_init_events(void)
-{
-	if (!xen_domain() || xen_events_irq < 0)
-		return -ENODEV;
-
-	xen_init_IRQ();
-
-	if (request_percpu_irq(xen_events_irq, xen_arm_callback,
-			"events", &xen_vcpu)) {
-		pr_err("Error requesting IRQ %d\n", xen_events_irq);
-		return -EINVAL;
-	}
-
-	on_each_cpu(xen_percpu_init, NULL, 0);
-
-	return 0;
-}
-postcore_initcall(xen_init_events);
-
 /* In the hypervisor.S file. */
 EXPORT_SYMBOL_GPL(HYPERVISOR_event_channel_op);
 EXPORT_SYMBOL_GPL(HYPERVISOR_grant_table_op);