Re: [PATCH v4 11/13] firmware: arm_sdei: add support for CPU private events

2017-11-01 Thread James Morse
Hi Will,

On 24/10/17 18:34, James Morse wrote:
> On 18/10/17 18:19, Will Deacon wrote:
>> On Tue, Oct 17, 2017 at 06:44:30PM +0100, James Morse wrote:
>>> Private SDE events are per-cpu, and need to be registered and enabled
>>> on each CPU.
>>>
>>> Hide this detail from the caller by adapting our {,un}register and
>>> {en,dis}able calls to send an IPI to each CPU if the event is private.
>>>
>>> CPU private events are unregistered when the CPU is powered-off, and
>>> re-registered when the CPU is brought back online. This saves bringing
>>> secondary cores back online to call private_reset() on shutdown, kexec
>>> and resume from hibernate.
> 
>>> diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c
>>> index 28e4c4cbb16d..5598d9ba8b5d 100644
>>> --- a/drivers/firmware/arm_sdei.c
>>> +++ b/drivers/firmware/arm_sdei.c
>>> @@ -610,6 +813,7 @@ static int sdei_device_freeze(struct device *dev)
>>>  {
>>> int err;
>>>  
>>> +   frozen = true;
>>> err = sdei_event_unregister_all();
> 
>> Are the release semantics from spin_unlock in sdei_event_unregister_all
>> sufficient for the ordering guarantees you need?
> 
> ... ordering ...
> 
> The hotplug notifiers don't touch that lock, so at the first level: no.
> 
> It looks like I was relying on the cpu-hotplug code using stop-machine for its
> work, and the spinlocks changing the pre-empt count for this to work. Which is
> not something I want to debug if it changes!
> 
> I'll post a patch changing this bool to a more sensible atomic type.

Here I show my ignorance: atomic_t didn't do what I thought. But I do take the
spinlock inside the hotplug callback, so I'll move the variable in there.


Thanks,

James
___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


Re: [PATCH v4 11/13] firmware: arm_sdei: add support for CPU private events

2017-10-24 Thread James Morse
Hi Will,

On 18/10/17 18:19, Will Deacon wrote:
> On Tue, Oct 17, 2017 at 06:44:30PM +0100, James Morse wrote:
>> Private SDE events are per-cpu, and need to be registered and enabled
>> on each CPU.
>>
>> Hide this detail from the caller by adapting our {,un}register and
>> {en,dis}able calls to send an IPI to each CPU if the event is private.
>>
>> CPU private events are unregistered when the CPU is powered-off, and
>> re-registered when the CPU is brought back online. This saves bringing
>> secondary cores back online to call private_reset() on shutdown, kexec
>> and resume from hibernate.

>> diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c
>> index 28e4c4cbb16d..5598d9ba8b5d 100644
>> --- a/drivers/firmware/arm_sdei.c
>> +++ b/drivers/firmware/arm_sdei.c
>> @@ -610,6 +813,7 @@ static int sdei_device_freeze(struct device *dev)
>>  {
>>  int err;
>>  
>> +frozen = true;
>>  err = sdei_event_unregister_all();

> Are the release semantics from spin_unlock in sdei_event_unregister_all
> sufficient for the ordering guarantees you need?

... ordering ...

The hotplug notifiers don't touch that lock, so at the first level: no.

It looks like I was relying on the cpu-hotplug code using stop-machine for its
work, and the spinlocks changing the pre-empt count for this to work. Which is
not something I want to debug if it changes!

I'll post a patch changing this bool to a more sensible atomic type.


Thanks,

James


___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


Re: [PATCH v4 11/13] firmware: arm_sdei: add support for CPU private events

2017-10-18 Thread Will Deacon
On Tue, Oct 17, 2017 at 06:44:30PM +0100, James Morse wrote:
> Private SDE events are per-cpu, and need to be registered and enabled
> on each CPU.
> 
> Hide this detail from the caller by adapting our {,un}register and
> {en,dis}able calls to send an IPI to each CPU if the event is private.
> 
> CPU private events are unregistered when the CPU is powered-off, and
> re-registered when the CPU is brought back online. This saves bringing
> secondary cores back online to call private_reset() on shutdown, kexec
> and resume from hibernate.
> 
> Signed-off-by: James Morse 
> ---
>  drivers/firmware/arm_sdei.c | 242 
> +---
>  1 file changed, 228 insertions(+), 14 deletions(-)
> 
> diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c
> index 28e4c4cbb16d..5598d9ba8b5d 100644
> --- a/drivers/firmware/arm_sdei.c
> +++ b/drivers/firmware/arm_sdei.c
> @@ -21,9 +21,11 @@
>  #include 
>  #include 
>  #include 
> +#include 
>  #include 
>  #include 
>  #include 
> +#include 
>  #include 
>  #include 
>  #include 
> @@ -64,12 +66,49 @@ struct sdei_event {
>   u8  priority;
>  
>   /* This pointer is handed to firmware as the event argument. */
> - struct sdei_registered_event *registered;
> + union {
> + /* Shared events */
> + struct sdei_registered_event *registered;
> +
> + /* CPU private events */
> + struct sdei_registered_event __percpu *private_registered;
> + };
>  };
>  
>  static LIST_HEAD(sdei_events);
>  static DEFINE_SPINLOCK(sdei_events_lock);
>  
> +/* When frozen, cpu-hotplug notifiers shouldn't unregister/re-register 
> events */
> +static bool frozen;

It looks like this can be accessed concurrently, so you need at least
READ_ONCE/WRITE_ONCE.

>  /* When entering idle, mask/unmask events for this cpu */
>  static int sdei_pm_notifier(struct notifier_block *nb, unsigned long action,
>   void *data)
> @@ -610,6 +813,7 @@ static int sdei_device_freeze(struct device *dev)
>  {
>   int err;
>  
> + frozen = true;
>   err = sdei_event_unregister_all();

Are the release semantics from spin_unlock in sdei_event_unregister_all
sufficient for the ordering guarantees you need?

Will
___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


Re: [PATCH v4 11/13] firmware: arm_sdei: add support for CPU private events

2017-10-18 Thread Catalin Marinas
On Tue, Oct 17, 2017 at 06:44:30PM +0100, James Morse wrote:
> Private SDE events are per-cpu, and need to be registered and enabled
> on each CPU.
> 
> Hide this detail from the caller by adapting our {,un}register and
> {en,dis}able calls to send an IPI to each CPU if the event is private.
> 
> CPU private events are unregistered when the CPU is powered-off, and
> re-registered when the CPU is brought back online. This saves bringing
> secondary cores back online to call private_reset() on shutdown, kexec
> and resume from hibernate.
> 
> Signed-off-by: James Morse 

Acked-by: Catalin Marinas 
___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v4 11/13] firmware: arm_sdei: add support for CPU private events

2017-10-17 Thread James Morse
Private SDE events are per-cpu, and need to be registered and enabled
on each CPU.

Hide this detail from the caller by adapting our {,un}register and
{en,dis}able calls to send an IPI to each CPU if the event is private.

CPU private events are unregistered when the CPU is powered-off, and
re-registered when the CPU is brought back online. This saves bringing
secondary cores back online to call private_reset() on shutdown, kexec
and resume from hibernate.

Signed-off-by: James Morse 
---
 drivers/firmware/arm_sdei.c | 242 +---
 1 file changed, 228 insertions(+), 14 deletions(-)

diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c
index 28e4c4cbb16d..5598d9ba8b5d 100644
--- a/drivers/firmware/arm_sdei.c
+++ b/drivers/firmware/arm_sdei.c
@@ -21,9 +21,11 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -64,12 +66,49 @@ struct sdei_event {
u8  priority;
 
/* This pointer is handed to firmware as the event argument. */
-   struct sdei_registered_event *registered;
+   union {
+   /* Shared events */
+   struct sdei_registered_event *registered;
+
+   /* CPU private events */
+   struct sdei_registered_event __percpu *private_registered;
+   };
 };
 
 static LIST_HEAD(sdei_events);
 static DEFINE_SPINLOCK(sdei_events_lock);
 
+/* When frozen, cpu-hotplug notifiers shouldn't unregister/re-register events 
*/
+static bool frozen;
+
+/* Private events are registered/enabled via IPI passing one of these */
+struct sdei_crosscall_args {
+   struct sdei_event *event;
+   atomic_t errors;
+   int first_error;
+};
+
+#define CROSSCALL_INIT(arg, event) (arg.event = event, \
+arg.first_error = 0, \
+atomic_set(&arg.errors, 0))
+
+static inline int sdei_do_cross_call(void *fn, struct sdei_event *event)
+{
+   struct sdei_crosscall_args arg;
+
+   CROSSCALL_INIT(arg, event);
+   on_each_cpu(fn, &arg, true);
+
+   return arg.first_error;
+}
+
+static inline void
+sdei_cross_call_return(struct sdei_crosscall_args *arg, int err)
+{
+   if (err && (atomic_inc_return(&arg->errors) == 1))
+   arg->first_error = err;
+}
+
 static int sdei_to_linux_errno(unsigned long sdei_err)
 {
switch (sdei_err) {
@@ -213,6 +252,26 @@ static struct sdei_event *sdei_event_create(u32 event_num,
reg->callback = cb;
reg->callback_arg = cb_arg;
event->registered = reg;
+   } else {
+   int cpu;
+   struct sdei_registered_event __percpu *regs;
+
+   regs = alloc_percpu(struct sdei_registered_event);
+   if (!regs) {
+   kfree(event);
+   return ERR_PTR(-ENOMEM);
+   }
+
+   for_each_possible_cpu(cpu) {
+   reg = per_cpu_ptr(regs, cpu);
+
+   reg->event_num = event->event_num;
+   reg->priority = event->priority;
+   reg->callback = cb;
+   reg->callback_arg = cb_arg;
+   }
+
+   event->private_registered = regs;
}
 
if (sdei_event_find(event_num)) {
@@ -234,6 +293,8 @@ static void sdei_event_destroy(struct sdei_event *event)
 
if (event->type == SDEI_EVENT_TYPE_SHARED)
kfree(event->registered);
+   else
+   free_percpu(event->private_registered);
 
kfree(event);
 }
@@ -264,11 +325,6 @@ static void _ipi_mask_cpu(void *ignored)
sdei_mask_local_cpu();
 }
 
-static int sdei_cpuhp_down(unsigned int ignored)
-{
-   return sdei_mask_local_cpu();
-}
-
 int sdei_unmask_local_cpu(void)
 {
int err;
@@ -290,11 +346,6 @@ static void _ipi_unmask_cpu(void *ignored)
sdei_unmask_local_cpu();
 }
 
-static int sdei_cpuhp_up(unsigned int ignored)
-{
-   return sdei_unmask_local_cpu();
-}
-
 static void _ipi_private_reset(void *ignored)
 {
int err;
@@ -345,6 +396,16 @@ static int sdei_api_event_enable(u32 event_num)
  0, NULL);
 }
 
+static void _ipi_event_enable(void *data)
+{
+   int err;
+   struct sdei_crosscall_args *arg = data;
+
+   err = sdei_api_event_enable(arg->event->event_num);
+
+   sdei_cross_call_return(arg, err);
+}
+
 int sdei_event_enable(u32 event_num)
 {
int err = -EINVAL;
@@ -356,6 +417,8 @@ int sdei_event_enable(u32 event_num)
err = -ENOENT;
else if (event->type == SDEI_EVENT_TYPE_SHARED)
err = sdei_api_event_enable(event->event_num);
+   else
+   err = sdei_do_cross_call(_ipi_event_enable, event);
spin_unlock(&sdei_events_lock);
 
return err;
@@ -368,6 +431,16 @@ static int sdei_