All of lore.kernel.org
 help / color / mirror / Atom feed
From: Daniel Thompson <daniel.thompson@linaro.org>
To: Will Deacon <will.deacon@arm.com>
Cc: Russell King <linux@arm.linux.org.uk>,
	"linux-arm-kernel@lists.infradead.org" 
	<linux-arm-kernel@lists.infradead.org>,
	"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
	Shawn Guo <shawn.guo@linaro.org>,
	Sascha Hauer <kernel@pengutronix.de>,
	Peter Zijlstra <a.p.zijlstra@chello.nl>,
	Paul Mackerras <paulus@samba.org>, Ingo Molnar <mingo@redhat.com>,
	Arnaldo Carvalho de Melo <acme@kernel.org>,
	Thomas Gleixner <tglx@linutronix.de>,
	Lucas Stach <l.stach@pengutronix.de>,
	Linus Walleij <linus.walleij@linaro.org>,
	Mark Rutland <Mark.Rutland@arm.com>,
	"patches@linaro.org" <patches@linaro.org>,
	"linaro-kernel@lists.linaro.org" <linaro-kernel@lists.linaro.org>,
	John Stultz <john.stultz@linaro.org>,
	Sumit Semwal <sumit.semwal@linaro.org>
Subject: Re: [PATCH v6] arm: perf: Directly handle SMP platforms with one SPI
Date: Thu, 02 Apr 2015 16:49:32 +0100	[thread overview]
Message-ID: <551D650C.8010409@linaro.org> (raw)
In-Reply-To: <20150331162040.GE24583@arm.com>

On 31/03/15 17:20, Will Deacon wrote:
> Hi Daniel,
>
> On Wed, Mar 04, 2015 at 01:25:45PM +0000, Daniel Thompson wrote:
>> Some ARM platforms mux the PMU interrupt of every core into a single
>> SPI. On such platforms if the PMU of any core except 0 raises an interrupt
>> then it cannot be serviced and eventually, if you are lucky, the spurious
>> irq detection might forcefully disable the interrupt.
>>
>> On these SoCs it is not possible to determine which core raised the
>> interrupt so workaround this issue by queuing irqwork on the other
>> cores whenever the primary interrupt handler is unable to service the
>> interrupt.
>>
>> The u8500 platform has an alternative workaround that dynamically alters
>> the affinity of the PMU interrupt. This workaround logic is no longer
>> required so the original code is removed as is the hook it relied upon.
>>
>> Tested on imx6q (which has fours cores/PMUs all muxed to a single SPI)
>> using a simple soak, combined perf and CPU hotplug soak and using
>> perf fuzzer's fast_repro.sh.
>
> [...]
>
>> diff --git a/arch/arm/include/asm/pmu.h b/arch/arm/include/asm/pmu.h
>> index b1596bd59129..dfef7904b790 100644
>> --- a/arch/arm/include/asm/pmu.h
>> +++ b/arch/arm/include/asm/pmu.h
>> @@ -87,6 +87,14 @@ struct pmu_hw_events {
>>           * already have to allocate this struct per cpu.
>>           */
>>          struct arm_pmu          *percpu_pmu;
>> +
>> +#ifdef CONFIG_SMP
>> +       /*
>> +        * This is used to schedule workaround logic on platforms where all
>> +        * the PMUs are attached to a single SPI.
>> +        */
>> +       struct irq_work work;
>> +#endif
>>   };
>>
>>   struct arm_pmu {
>> @@ -117,6 +125,10 @@ struct arm_pmu {
>>          struct platform_device  *plat_device;
>>          struct pmu_hw_events    __percpu *hw_events;
>>          struct notifier_block   hotplug_nb;
>> +#ifdef CONFIG_SMP
>> +       int                     muxed_spi_workaround_irq;
>> +       atomic_t                remaining_irq_work;
>> +#endif
>>   };
>>
>>   #define to_arm_pmu(p) (container_of(p, struct arm_pmu, pmu))
>> diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c
>> index 557e128e4df0..e3fc1a0ce969 100644
>> --- a/arch/arm/kernel/perf_event.c
>> +++ b/arch/arm/kernel/perf_event.c
>> @@ -305,8 +305,6 @@ validate_group(struct perf_event *event)
>>   static irqreturn_t armpmu_dispatch_irq(int irq, void *dev)
>>   {
>>          struct arm_pmu *armpmu;
>> -       struct platform_device *plat_device;
>> -       struct arm_pmu_platdata *plat;
>>          int ret;
>>          u64 start_clock, finish_clock;
>>
>> @@ -317,14 +315,9 @@ static irqreturn_t armpmu_dispatch_irq(int irq, void *dev)
>>           * dereference.
>>           */
>>          armpmu = *(void **)dev;
>> -       plat_device = armpmu->plat_device;
>> -       plat = dev_get_platdata(&plat_device->dev);
>>
>>          start_clock = sched_clock();
>> -       if (plat && plat->handle_irq)
>> -               ret = plat->handle_irq(irq, armpmu, armpmu->handle_irq);
>> -       else
>> -               ret = armpmu->handle_irq(irq, armpmu);
>> +       ret = armpmu->handle_irq(irq, armpmu);
>>          finish_clock = sched_clock();
>>
>>          perf_sample_event_took(finish_clock - start_clock);
>> diff --git a/arch/arm/kernel/perf_event_cpu.c b/arch/arm/kernel/perf_event_cpu.c
>> index 61b53c46edfa..d5bbd79abd4c 100644
>> --- a/arch/arm/kernel/perf_event_cpu.c
>> +++ b/arch/arm/kernel/perf_event_cpu.c
>> @@ -59,6 +59,116 @@ int perf_num_counters(void)
>>   }
>>   EXPORT_SYMBOL_GPL(perf_num_counters);
>>
>> +#ifdef CONFIG_SMP
>> +
>> +static cpumask_t down_prepare_cpu_mask;
>> +static DEFINE_SPINLOCK(down_prepare_cpu_lock);
>> +
>> +/*
>> + * Workaround logic that is distributed to all cores if the PMU has only
>> + * a single IRQ and the CPU receiving that IRQ cannot handle it. Its
>> + * job is to try to service the interrupt on the current CPU. It will
>> + * also enable the IRQ again if all the other CPUs have already tried to
>> + * service it.
>> + */
>> +static void cpu_pmu_do_percpu_work(struct irq_work *w)
>> +{
>> +       struct pmu_hw_events *hw_events =
>> +           container_of(w, struct pmu_hw_events, work);
>> +       struct arm_pmu *cpu_pmu = hw_events->percpu_pmu;
>> +
>> +       /* Ignore the return code, we can do nothing useful with it */
>> +       (void) cpu_pmu->handle_irq(0, cpu_pmu);
>> +
>> +       if (atomic_dec_and_test(&cpu_pmu->remaining_irq_work))
>> +               enable_irq(cpu_pmu->muxed_spi_workaround_irq);
>> +}
>> +
>> +/*
>> + * Workaround for systems where all PMU interrupts are targeting a
>> + * single SPI.
>> + *
>> + * The workaround will disable the interrupt and distribute irqwork to all
>> + * the other processors in the system. Hopefully one of them will clear the
>> + * interrupt...
>> + *
>> + * The workaround is only deployed when all PMU interrupts are aimed
>> + * at a single core. As a result the workaround is never re-entered
>> + * making it safe for us to use static data to maintain state.
>> + */
>> +static void cpu_pmu_deploy_muxed_spi_workaround(struct arm_pmu *cpu_pmu)
>> +{
>> +       static cpumask_t irqwork_mask;
>> +       int cpu;
>> +
>> +       disable_irq_nosync(cpu_pmu->muxed_spi_workaround_irq);
>> +       spin_lock(&down_prepare_cpu_lock);
>> +
>> +       /*
>> +        * Combining cpu_online_mask and down_prepare_cpu_mask gives
>> +        * us the CPUs that are currently online and cannot die until
>> +        * we release down_prepare_cpu_lock.
>> +        */
>> +       cpumask_andnot(&irqwork_mask, cpu_online_mask, &down_prepare_cpu_mask);
>> +       cpumask_clear_cpu(smp_processor_id(), &irqwork_mask);
>> +       atomic_add(cpumask_weight(&irqwork_mask), &cpu_pmu->remaining_irq_work);
>
> AFAICT, this is a hack to avoid get_online_cpus (which can sleep) from irq
> context? Is there no way we can do try_get_online_cpus, and just return
> IRQ_NONE if that fails? At some point, the hotplug operation will complete
> and we'll be able to service the pending interrupt. I think that would allow
> us to kill the down_prepare_cpu_lock.

It certainly doesn't look easy but I will take a closer look...

 From the perf side its reasonably easy. I don't think we can return 
IRQ_NONE (we might own the h.p. lock on the CPU handling the interrupt) 
so we have to disable the interrupt. However we could probably arrange 
to re-enable it from the hotplug notifications when this is needed.

Unfortunately I'm not sure the cpu_hotplug side is so easy.

WARNING: multiple messages have this Message-ID (diff)
From: daniel.thompson@linaro.org (Daniel Thompson)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH v6] arm: perf: Directly handle SMP platforms with one SPI
Date: Thu, 02 Apr 2015 16:49:32 +0100	[thread overview]
Message-ID: <551D650C.8010409@linaro.org> (raw)
In-Reply-To: <20150331162040.GE24583@arm.com>

On 31/03/15 17:20, Will Deacon wrote:
> Hi Daniel,
>
> On Wed, Mar 04, 2015 at 01:25:45PM +0000, Daniel Thompson wrote:
>> Some ARM platforms mux the PMU interrupt of every core into a single
>> SPI. On such platforms if the PMU of any core except 0 raises an interrupt
>> then it cannot be serviced and eventually, if you are lucky, the spurious
>> irq detection might forcefully disable the interrupt.
>>
>> On these SoCs it is not possible to determine which core raised the
>> interrupt so workaround this issue by queuing irqwork on the other
>> cores whenever the primary interrupt handler is unable to service the
>> interrupt.
>>
>> The u8500 platform has an alternative workaround that dynamically alters
>> the affinity of the PMU interrupt. This workaround logic is no longer
>> required so the original code is removed as is the hook it relied upon.
>>
>> Tested on imx6q (which has fours cores/PMUs all muxed to a single SPI)
>> using a simple soak, combined perf and CPU hotplug soak and using
>> perf fuzzer's fast_repro.sh.
>
> [...]
>
>> diff --git a/arch/arm/include/asm/pmu.h b/arch/arm/include/asm/pmu.h
>> index b1596bd59129..dfef7904b790 100644
>> --- a/arch/arm/include/asm/pmu.h
>> +++ b/arch/arm/include/asm/pmu.h
>> @@ -87,6 +87,14 @@ struct pmu_hw_events {
>>           * already have to allocate this struct per cpu.
>>           */
>>          struct arm_pmu          *percpu_pmu;
>> +
>> +#ifdef CONFIG_SMP
>> +       /*
>> +        * This is used to schedule workaround logic on platforms where all
>> +        * the PMUs are attached to a single SPI.
>> +        */
>> +       struct irq_work work;
>> +#endif
>>   };
>>
>>   struct arm_pmu {
>> @@ -117,6 +125,10 @@ struct arm_pmu {
>>          struct platform_device  *plat_device;
>>          struct pmu_hw_events    __percpu *hw_events;
>>          struct notifier_block   hotplug_nb;
>> +#ifdef CONFIG_SMP
>> +       int                     muxed_spi_workaround_irq;
>> +       atomic_t                remaining_irq_work;
>> +#endif
>>   };
>>
>>   #define to_arm_pmu(p) (container_of(p, struct arm_pmu, pmu))
>> diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c
>> index 557e128e4df0..e3fc1a0ce969 100644
>> --- a/arch/arm/kernel/perf_event.c
>> +++ b/arch/arm/kernel/perf_event.c
>> @@ -305,8 +305,6 @@ validate_group(struct perf_event *event)
>>   static irqreturn_t armpmu_dispatch_irq(int irq, void *dev)
>>   {
>>          struct arm_pmu *armpmu;
>> -       struct platform_device *plat_device;
>> -       struct arm_pmu_platdata *plat;
>>          int ret;
>>          u64 start_clock, finish_clock;
>>
>> @@ -317,14 +315,9 @@ static irqreturn_t armpmu_dispatch_irq(int irq, void *dev)
>>           * dereference.
>>           */
>>          armpmu = *(void **)dev;
>> -       plat_device = armpmu->plat_device;
>> -       plat = dev_get_platdata(&plat_device->dev);
>>
>>          start_clock = sched_clock();
>> -       if (plat && plat->handle_irq)
>> -               ret = plat->handle_irq(irq, armpmu, armpmu->handle_irq);
>> -       else
>> -               ret = armpmu->handle_irq(irq, armpmu);
>> +       ret = armpmu->handle_irq(irq, armpmu);
>>          finish_clock = sched_clock();
>>
>>          perf_sample_event_took(finish_clock - start_clock);
>> diff --git a/arch/arm/kernel/perf_event_cpu.c b/arch/arm/kernel/perf_event_cpu.c
>> index 61b53c46edfa..d5bbd79abd4c 100644
>> --- a/arch/arm/kernel/perf_event_cpu.c
>> +++ b/arch/arm/kernel/perf_event_cpu.c
>> @@ -59,6 +59,116 @@ int perf_num_counters(void)
>>   }
>>   EXPORT_SYMBOL_GPL(perf_num_counters);
>>
>> +#ifdef CONFIG_SMP
>> +
>> +static cpumask_t down_prepare_cpu_mask;
>> +static DEFINE_SPINLOCK(down_prepare_cpu_lock);
>> +
>> +/*
>> + * Workaround logic that is distributed to all cores if the PMU has only
>> + * a single IRQ and the CPU receiving that IRQ cannot handle it. Its
>> + * job is to try to service the interrupt on the current CPU. It will
>> + * also enable the IRQ again if all the other CPUs have already tried to
>> + * service it.
>> + */
>> +static void cpu_pmu_do_percpu_work(struct irq_work *w)
>> +{
>> +       struct pmu_hw_events *hw_events =
>> +           container_of(w, struct pmu_hw_events, work);
>> +       struct arm_pmu *cpu_pmu = hw_events->percpu_pmu;
>> +
>> +       /* Ignore the return code, we can do nothing useful with it */
>> +       (void) cpu_pmu->handle_irq(0, cpu_pmu);
>> +
>> +       if (atomic_dec_and_test(&cpu_pmu->remaining_irq_work))
>> +               enable_irq(cpu_pmu->muxed_spi_workaround_irq);
>> +}
>> +
>> +/*
>> + * Workaround for systems where all PMU interrupts are targeting a
>> + * single SPI.
>> + *
>> + * The workaround will disable the interrupt and distribute irqwork to all
>> + * the other processors in the system. Hopefully one of them will clear the
>> + * interrupt...
>> + *
>> + * The workaround is only deployed when all PMU interrupts are aimed
>> + * at a single core. As a result the workaround is never re-entered
>> + * making it safe for us to use static data to maintain state.
>> + */
>> +static void cpu_pmu_deploy_muxed_spi_workaround(struct arm_pmu *cpu_pmu)
>> +{
>> +       static cpumask_t irqwork_mask;
>> +       int cpu;
>> +
>> +       disable_irq_nosync(cpu_pmu->muxed_spi_workaround_irq);
>> +       spin_lock(&down_prepare_cpu_lock);
>> +
>> +       /*
>> +        * Combining cpu_online_mask and down_prepare_cpu_mask gives
>> +        * us the CPUs that are currently online and cannot die until
>> +        * we release down_prepare_cpu_lock.
>> +        */
>> +       cpumask_andnot(&irqwork_mask, cpu_online_mask, &down_prepare_cpu_mask);
>> +       cpumask_clear_cpu(smp_processor_id(), &irqwork_mask);
>> +       atomic_add(cpumask_weight(&irqwork_mask), &cpu_pmu->remaining_irq_work);
>
> AFAICT, this is a hack to avoid get_online_cpus (which can sleep) from irq
> context? Is there no way we can do try_get_online_cpus, and just return
> IRQ_NONE if that fails? At some point, the hotplug operation will complete
> and we'll be able to service the pending interrupt. I think that would allow
> us to kill the down_prepare_cpu_lock.

It certainly doesn't look easy but I will take a closer look...

 From the perf side its reasonably easy. I don't think we can return 
IRQ_NONE (we might own the h.p. lock on the CPU handling the interrupt) 
so we have to disable the interrupt. However we could probably arrange 
to re-enable it from the hotplug notifications when this is needed.

Unfortunately I'm not sure the cpu_hotplug side is so easy.

  reply	other threads:[~2015-04-02 15:49 UTC|newest]

Thread overview: 40+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-11-21 14:53 [PATCH] arm: perf: Directly handle SMP platforms with one SPI Daniel Thompson
2014-11-21 14:53 ` Daniel Thompson
2014-11-26 16:59 ` Daniel Thompson
2014-11-26 16:59   ` Daniel Thompson
2014-12-02 14:49   ` Mark Rutland
2014-12-02 14:49     ` Mark Rutland
2014-12-02 21:33     ` Daniel Thompson
2014-12-02 21:33       ` Daniel Thompson
2014-12-02 13:22 ` Linus Walleij
2014-12-02 13:22   ` Linus Walleij
2015-01-07 12:28 ` [PATCH v3] " Daniel Thompson
2015-01-07 12:28   ` Daniel Thompson
2015-01-08 17:30   ` Will Deacon
2015-01-08 17:30     ` Will Deacon
2015-01-09 14:23     ` Daniel Thompson
2015-01-09 14:23       ` Daniel Thompson
2015-01-09 16:16 ` [PATCH v4] " Daniel Thompson
2015-01-09 16:16   ` Daniel Thompson
2015-01-16 10:58   ` Will Deacon
2015-01-16 10:58     ` Will Deacon
2015-01-16 11:28     ` Daniel Thompson
2015-01-16 11:28       ` Daniel Thompson
2015-01-20 12:25 ` [PATCH v5] " Daniel Thompson
2015-01-20 12:25   ` Daniel Thompson
2015-01-23 17:25   ` Mark Rutland
2015-01-23 17:25     ` Mark Rutland
2015-02-24 16:11     ` Daniel Thompson
2015-02-24 16:11       ` Daniel Thompson
2015-03-04 13:25 ` [PATCH v6] " Daniel Thompson
2015-03-04 13:25   ` Daniel Thompson
2015-03-31 16:20   ` Will Deacon
2015-03-31 16:20     ` Will Deacon
2015-04-02 15:49     ` Daniel Thompson [this message]
2015-04-02 15:49       ` Daniel Thompson
2015-03-31 17:08   ` Mark Rutland
2015-03-31 17:08     ` Mark Rutland
2015-04-02 15:27     ` Daniel Thompson
2015-04-02 15:27       ` Daniel Thompson
2015-04-02 16:44       ` Mark Rutland
2015-04-02 16:44         ` Mark Rutland

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=551D650C.8010409@linaro.org \
    --to=daniel.thompson@linaro.org \
    --cc=Mark.Rutland@arm.com \
    --cc=a.p.zijlstra@chello.nl \
    --cc=acme@kernel.org \
    --cc=john.stultz@linaro.org \
    --cc=kernel@pengutronix.de \
    --cc=l.stach@pengutronix.de \
    --cc=linaro-kernel@lists.linaro.org \
    --cc=linus.walleij@linaro.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux@arm.linux.org.uk \
    --cc=mingo@redhat.com \
    --cc=patches@linaro.org \
    --cc=paulus@samba.org \
    --cc=shawn.guo@linaro.org \
    --cc=sumit.semwal@linaro.org \
    --cc=tglx@linutronix.de \
    --cc=will.deacon@arm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.