All of lore.kernel.org
 help / color / mirror / Atom feed
From: Philippe Gerum <rpm@xenomai.org>
To: Florian Bezdeka <florian.bezdeka@siemens.com>
Cc: Andrew MacPherson <andrew@elk.audio>,
	 xenomai@lists.linux.dev,  Gerte Hoogewerf <ghoogewerf@lmi3d.com>
Subject: Re: perf getting all-zeroes IP on STM32MP1
Date: Wed, 09 Sep 2026 10:21:32 +0200	[thread overview]
Message-ID: <87mrtq25tf.fsf@xenomai.org> (raw)
In-Reply-To: <f78cec8dff20c4c799ede0e8e749e97ccf96ac27.camel@siemens.com> (Florian Bezdeka's message of "Tue, 08 Sep 2026 10:46:52 +0200")

Florian Bezdeka <florian.bezdeka@siemens.com> writes:

> On Tue, 2026-09-08 at 10:16 +0200, Philippe Gerum wrote:
>> 
>> > 
>> > We could ask the clock_event_device (real device) for it's IRQ and mark
>> > it  as __IRQF_TIMER during proxy registration.
>> > 
>> > The following seems to work, but might need some more work.
>> > 
>> > The on_each_cpu() part is likely a overkill due to percpu IRQs, but the
>> > real_dev part of mark_timer_tick_irq() is local CPU specific as well.
>> > 
>> > diff --git a/kernel/time/tick-proxy.c b/kernel/time/tick-proxy.c
>> > index 6ef04fcf5dacd..55526cd99e849 100644
>> > --- a/kernel/time/tick-proxy.c
>> > +++ b/kernel/time/tick-proxy.c
>> > @@ -296,6 +296,22 @@ static int enable_oob_timer(void *arg) /* hard_irqs_disabled() */
>> >  	return 0;
>> >  }
>> >  
>> > +static void mark_timer_tick_irq(void *arg)
>> > +{
>> > +	struct clock_event_device *real_dev;
>> > +	struct irq_desc *desc;
>> > +	int irq;
>> > +
>> > +	real_dev = raw_cpu_ptr(&tick_cpu_device)->evtdev;
>> > +	irq = real_dev->irq;
>> > +	desc = irq_to_desc(irq);
>> > +
>> > +	if (!desc || !desc->action)
>> > +		return;
>> > +
>> > +	desc->action->flags |= __IRQF_TIMER;
>> > +}
>> > +
>> >  struct proxy_install_arg {
>> >  	void (*setup_proxy)(struct clock_proxy_device *dev);
>> >  	int result;
>> > @@ -400,6 +416,8 @@ int tick_install_proxy(void (*setup_proxy)(struct clock_proxy_device *dev),
>> >  		return arg.result;
>> >  	}
>> >  
>> > +	on_each_cpu(mark_timer_tick_irq, NULL, true);
>> > +
>> >  	/*
>> >  	 * Start ticking from the out-of-band interrupt stage upon
>> >  	 * receipt of out-of-band timer events.
>> 
>> Almost there, but we still need to provide the registers used in
>> profiling when no proxy is registered, in which case we cannot depend on
>> proxy registration for this, but on clock event device registration
>> instead.
>
> diff --git a/kernel/irq/pipeline.c b/kernel/irq/pipeline.c
> index 85ec0cbf5fb1e..c4eda1243b915 100644
> --- a/kernel/irq/pipeline.c
> +++ b/kernel/irq/pipeline.c
> @@ -1064,7 +1064,7 @@ void copy_timer_regs(struct irq_desc *desc, struct pt_regs *regs)
>  {
>  	struct irq_pipeline_data *p;
>  
> -	if (desc->action == NULL || !(desc->action->flags & __IRQF_TIMER))
> +	if (desc->action == NULL || !(desc->action->flags & IRQF_DEFERRED_TIMER))
>  		return;
>  	/*
>  	 * Given our deferred dispatching model for regular IRQs, we
> diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c
> index 90fae659e4ea6..9296164c1d198 100644
> --- a/kernel/time/tick-common.c
> +++ b/kernel/time/tick-common.c
> @@ -326,6 +326,16 @@ bool tick_check_replacement(struct clock_event_device *curdev,
>  	return tick_check_preferred(curdev, newdev);
>  }
>  
> +static void tick_mark_deferred_timer_irq(struct clock_event_device *dev)
> +{
> +	struct irq_desc *desc = irq_to_desc(dev->irq);
> +
> +	if (!desc || !desc->action)
> +		return;
> +
> +	desc->action->flags |= IRQF_DEFERRED_TIMER;
> +}
> +
>  /*
>   * Check, if the new registered device should be used. Called with
>   * clockevents_lock held and interrupts disabled.
> @@ -367,6 +377,8 @@ void tick_check_new_device(struct clock_event_device *newdev)
>  	tick_setup_device(td, newdev, cpu, cpumask_of(cpu));
>  	if (newdev->features & CLOCK_EVT_FEAT_ONESHOT)
>  		tick_oneshot_notify();
> +	if (newdev->features & CLOCK_EVT_FEAT_PIPELINE)
> +		tick_mark_deferred_timer_irq(newdev);
>  	return;
>  
>  out_bc:

I would attach this flag to the interrupt descriptor instead because
this is actually a property of the interrupt line, not of its
handler(s). Also, we need to consider device shutdown: as the current
tick source may be replaced dynamically, turning off this bit for proper
accounting when a clock device goes down would be safer.

e.g.:

diff --git a/include/linux/irq.h b/include/linux/irq.h
index b13e4e90ab18f..5f7a2c78b3ca7 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -81,6 +81,7 @@ enum irqchip_irq_state;
  *                                when pipelining is enabled (CONFIG_IRQ_PIPELINE),
  *                                regardless of the (virtualized) interrupt state
  *                                maintained by local_irq_save/disable().
+ * IRQ_TICK                     - Interrupt is a timer tick source.
  */
 enum {
 	IRQ_TYPE_NONE		= 0x00000000,
@@ -109,14 +110,15 @@ enum {
 	IRQ_HIDDEN		= (1 << 20),
 	IRQ_NO_DEBUG		= (1 << 21),
 	IRQ_OOB			= (1 << 22),
-	IRQ_RESERVED		= (1 << 23),
+	IRQ_TICK		= (1 << 23),
+	IRQ_RESERVED		= (1 << 24),
 };
 
 #define IRQF_MODIFY_MASK	\
 	(IRQ_TYPE_SENSE_MASK | IRQ_NOPROBE | IRQ_NOREQUEST | \
 	 IRQ_NOAUTOEN | IRQ_LEVEL | IRQ_NO_BALANCING | \
 	 IRQ_PER_CPU | IRQ_NESTED_THREAD | IRQ_NOTHREAD | IRQ_PER_CPU_DEVID | \
-	 IRQ_IS_POLLED | IRQ_DISABLE_UNLAZY | IRQ_HIDDEN | IRQ_OOB)
+	 IRQ_IS_POLLED | IRQ_DISABLE_UNLAZY | IRQ_HIDDEN | IRQ_OOB | IRQ_TICK)
 
 #define IRQ_NO_BALANCING_MASK	(IRQ_PER_CPU | IRQ_NO_BALANCING)
 
@@ -1258,6 +1260,7 @@ static inline struct irq_chip_type *irq_data_get_chip_type(struct irq_data *d)
 
 #ifdef CONFIG_IRQ_PIPELINE
 int irq_switch_oob(unsigned int irq, bool on);
+void irq_switch_tick(unsigned int irq, bool on);
 void irq_clear_deferral(struct irq_desc *desc);
 void irq_clear_forward(struct irq_desc *desc);
 #else
@@ -1266,6 +1269,10 @@ static inline int irq_switch_oob(unsigned int irq, bool on)
 	return 0;
 }
 
+static inline void irq_switch_tick(unsigned int irq, bool on)
+{
+}
+
 static inline void irq_clear_deferral(struct irq_desc *desc) { }
 static inline void irq_clear_forward(struct irq_desc *desc) { }
 #endif	/* !CONFIG_IRQ_PIPELINE */
diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h
index 260ffd288bd81..c4e2d6a5d20d5 100644
--- a/include/linux/irqdesc.h
+++ b/include/linux/irqdesc.h
@@ -274,6 +274,11 @@ static inline int irq_is_oob(unsigned int irq)
 	return irq_check_status_bit(irq, IRQ_OOB);
 }
 
+static inline int irq_is_tick(unsigned int irq)
+{
+	return irq_check_status_bit(irq, IRQ_TICK);
+}
+
 void __irq_set_lockdep_class(unsigned int irq, struct lock_class_key *lock_class,
 			     struct lock_class_key *request_class);
 static inline void
diff --git a/kernel/irq/debug.h b/kernel/irq/debug.h
index 4eafd04a62962..9813c8ec66a44 100644
--- a/kernel/irq/debug.h
+++ b/kernel/irq/debug.h
@@ -34,6 +34,7 @@ static inline void print_irq_desc(unsigned int irq, struct irq_desc *desc)
 	___P(IRQ_NOTHREAD);
 	___P(IRQ_NOAUTOEN);
 	___P(IRQ_OOB);
+	___P(IRQ_TICK);
 
 	___PS(IRQS_AUTODETECT);
 	___PS(IRQS_REPLAY);
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index 1c1920f5fbc08..a10d1081bea8c 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -949,6 +949,17 @@ int irq_switch_oob(unsigned int irq, bool on)
 }
 EXPORT_SYMBOL_GPL(irq_switch_oob);
 
+void irq_switch_tick(unsigned int irq, bool on)
+{
+	scoped_irqdesc_get_and_lock(irq, 0) {
+		if (on)
+			irq_settings_set_tick(scoped_irqdesc);
+		else
+			irq_settings_clr_tick(scoped_irqdesc);
+	}
+}
+EXPORT_SYMBOL_GPL(irq_switch_tick);
+
 #endif /* CONFIG_IRQ_PIPELINE */
 
 /*
diff --git a/kernel/irq/pipeline.c b/kernel/irq/pipeline.c
index 85ec0cbf5fb1e..ce4404b7b2c4d 100644
--- a/kernel/irq/pipeline.c
+++ b/kernel/irq/pipeline.c
@@ -1062,10 +1062,6 @@ bool handle_oob_irq(struct irq_desc *desc)
 static inline
 void copy_timer_regs(struct irq_desc *desc, struct pt_regs *regs)
 {
-	struct irq_pipeline_data *p;
-
-	if (desc->action == NULL || !(desc->action->flags & __IRQF_TIMER))
-		return;
 	/*
 	 * Given our deferred dispatching model for regular IRQs, we
 	 * record the preempted context registers only for the latest
@@ -1073,8 +1069,10 @@ void copy_timer_regs(struct irq_desc *desc, struct pt_regs *regs)
 	 * CPU times properly. It is assumed that no other interrupt
 	 * handler cares for such information.
 	 */
-	p = raw_cpu_ptr(&irq_pipeline);
-	arch_save_timer_regs(&p->tick_regs, regs);
+	if (irq_settings_is_tick(desc)) {
+		struct irq_pipeline_data *p = raw_cpu_ptr(&irq_pipeline);
+		arch_save_timer_regs(&p->tick_regs, regs);
+	}
 }
 
 static __always_inline
diff --git a/kernel/irq/settings.h b/kernel/irq/settings.h
index 27a37d992f237..c4b48d22d271a 100644
--- a/kernel/irq/settings.h
+++ b/kernel/irq/settings.h
@@ -19,6 +19,7 @@ enum {
 	_IRQ_HIDDEN		= IRQ_HIDDEN,
 	_IRQ_NO_DEBUG		= IRQ_NO_DEBUG,
 	_IRQ_OOB		= IRQ_OOB,
+	_IRQ_TICK		= IRQ_TICK,
 	_IRQ_PROC_VALID		= IRQ_RESERVED,
 	_IRQF_MODIFY_MASK	= IRQF_MODIFY_MASK,
 };
@@ -37,6 +38,7 @@ enum {
 #define IRQ_HIDDEN		GOT_YOU_MORON
 #define IRQ_NO_DEBUG		GOT_YOU_MORON
 #define IRQ_OOB			GOT_YOU_MORON
+#define IRQ_TICK		GOT_YOU_MORON
 #define IRQ_RESERVED		GOT_YOU_MORON
 #undef IRQF_MODIFY_MASK
 #define IRQF_MODIFY_MASK	GOT_YOU_MORON
@@ -210,3 +212,18 @@ static inline void irq_settings_set_oob(struct irq_desc *desc)
 {
 	desc->status_use_accessors |= _IRQ_OOB;
 }
+
+static inline bool irq_settings_is_tick(struct irq_desc *desc)
+{
+	return desc->status_use_accessors & _IRQ_TICK;
+}
+
+static inline void irq_settings_clr_tick(struct irq_desc *desc)
+{
+	desc->status_use_accessors &= ~_IRQ_TICK;
+}
+
+static inline void irq_settings_set_tick(struct irq_desc *desc)
+{
+	desc->status_use_accessors |= _IRQ_TICK;
+}
diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c
index 0ed4122d40986..d8d2dd43baaa0 100644
--- a/kernel/time/clockevents.c
+++ b/kernel/time/clockevents.c
@@ -12,6 +12,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/smp.h>
+#include <linux/irq.h>
 #include <linux/device.h>
 
 #include "tick-internal.h"
@@ -177,6 +178,8 @@ void clockevents_shutdown(struct clock_event_device *dev)
 	clockevents_switch_state(dev, CLOCK_EVT_STATE_SHUTDOWN);
 	dev->next_event = KTIME_MAX;
 	dev->next_event_forced = 0;
+	if (dev->features & CLOCK_EVT_FEAT_PIPELINE)
+		irq_switch_tick(dev->irq, false);
 }
 
 /**
diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c
index 90fae659e4ea6..d15bd478cef89 100644
--- a/kernel/time/tick-common.c
+++ b/kernel/time/tick-common.c
@@ -13,6 +13,7 @@
 #include <linux/hrtimer.h>
 #include <linux/interrupt.h>
 #include <linux/nmi.h>
+#include <linux/irq.h>
 #include <linux/percpu.h>
 #include <linux/profile.h>
 #include <linux/sched.h>
@@ -244,6 +245,9 @@ static void tick_setup_device(struct tick_device *td,
 	if (!cpumask_equal(newdev->cpumask, cpumask))
 		irq_set_affinity(newdev->irq, cpumask);
 
+	if (newdev->features & CLOCK_EVT_FEAT_PIPELINE)
+		irq_switch_tick(newdev->irq, true);
+
 	/*
 	 * When global broadcasting is active, check if the current
 	 * device is registered as a placeholder for broadcast mode.

-- 
Philippe.

  reply	other threads:[~2026-09-09  8:21 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-04 13:26 perf getting all-zeroes IP on STM32MP1 Andrew MacPherson
2026-09-04 14:08 ` Florian Bezdeka
2026-09-04 15:17   ` Philippe Gerum
2026-09-04 15:55     ` Florian Bezdeka
2026-09-05  9:16       ` Philippe Gerum
2026-09-07  8:57   ` Andrew MacPherson
2026-09-07 11:34     ` Florian Bezdeka
2026-09-07 12:03       ` Gerte Hoogewerf
2026-09-07 12:41       ` Philippe Gerum
2026-09-07 14:39         ` Florian Bezdeka
2026-09-07 15:00           ` Philippe Gerum
2026-09-08  8:02             ` Florian Bezdeka
2026-09-08  8:16               ` Philippe Gerum
2026-09-08  8:46                 ` Florian Bezdeka
2026-09-09  8:21                   ` Philippe Gerum [this message]
2026-09-09  8:29                     ` Florian Bezdeka
2026-09-09  8:49                       ` Philippe Gerum
2026-09-08  8:11         ` Philippe Gerum
2026-09-07 12:51       ` Andrew MacPherson

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=87mrtq25tf.fsf@xenomai.org \
    --to=rpm@xenomai.org \
    --cc=andrew@elk.audio \
    --cc=florian.bezdeka@siemens.com \
    --cc=ghoogewerf@lmi3d.com \
    --cc=xenomai@lists.linux.dev \
    /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.