Merge tag 'irq-core-2026-02-09' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull irq core updates from Thomas Gleixner:
 "Updates for the interrupt core subsystem:

   - Remove the interrupt timing infrastructure

     This was added seven years ago to be used for power management
     purposes, but that integration never happened.

   - Clean up the remaining setup_percpu_irq() users

     The memory allocator is available when interrupts can be requested
     so there is not need for static irq_action. Move the remaining
     users to request_percpu_irq() and delete the historical cruft.

   - Warn when interrupt flag inconsistencies are detected in
     request*_irq().

     Inconsistent flags can lead to hard to diagnose malfunction. The
     fallout of this new warning has been addressed in next and the
     fixes are coming in via the maintainer trees and the tip
     irq/cleanup pull requests.

   - Invoke affinity notifier when CPU hotplug breaks affinity

     Otherwise the code using the notifier misses the affinity change
     and operates on stale information.

   - The usual cleanups and improvements"

* tag 'irq-core-2026-02-09' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  genirq/proc: Replace snprintf with strscpy in register_handler_proc
  genirq/cpuhotplug: Notify about affinity changes breaking the affinity mask
  genirq: Move clear of kstat_irqs to free_desc()
  genirq: Warn about using IRQF_ONESHOT without a threaded handler
  irqdomain: Fix up const problem in irq_domain_set_name()
  genirq: Remove setup_percpu_irq()
  clocksource/drivers/mips-gic-timer: Move GIC timer to request_percpu_irq()
  MIPS: Move IP27 timer to request_percpu_irq()
  MIPS: Move IP30 timer to request_percpu_irq()
  genirq: Remove __request_percpu_irq() helper
  genirq: Remove IRQ timing tracking infrastructure
This commit is contained in:
Linus Torvalds
2026-02-10 13:39:37 -08:00
18 changed files with 53 additions and 1200 deletions
-1
View File
@@ -23,7 +23,6 @@ void mips_event_handler(struct clock_event_device *dev);
int c0_compare_int_usable(void);
irqreturn_t c0_compare_interrupt(int, void *);
extern struct irqaction c0_compare_irqaction;
extern int cp0_timer_irq_installed;
#endif /* __ASM_CEVT_R4K_H */
-11
View File
@@ -159,17 +159,6 @@ irqreturn_t c0_compare_interrupt(int irq, void *dev_id)
return IRQ_NONE;
}
struct irqaction c0_compare_irqaction = {
.handler = c0_compare_interrupt,
/*
* IRQF_SHARED: The timer interrupt may be shared with other interrupts
* such as perf counter and FDC interrupts.
*/
.flags = IRQF_PERCPU | IRQF_TIMER | IRQF_SHARED,
.name = "timer",
};
void mips_event_handler(struct clock_event_device *dev)
{
}
+2 -8
View File
@@ -58,13 +58,6 @@ static irqreturn_t hub_rt_counter_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}
struct irqaction hub_rt_irqaction = {
.handler = hub_rt_counter_handler,
.percpu_dev_id = &hub_rt_clockevent,
.flags = IRQF_PERCPU | IRQF_TIMER,
.name = "hub-rt",
};
/*
* This is a hack; we really need to figure these values out dynamically
*
@@ -103,7 +96,8 @@ static void __init hub_rt_clock_event_global_init(void)
{
irq_set_handler(IP27_RT_TIMER_IRQ, handle_percpu_devid_irq);
irq_set_percpu_devid(IP27_RT_TIMER_IRQ);
setup_percpu_irq(IP27_RT_TIMER_IRQ, &hub_rt_irqaction);
WARN_ON(request_percpu_irq(IP27_RT_TIMER_IRQ, hub_rt_counter_handler,
"hub-rt", &hub_rt_clockevent));
}
static u64 hub_rt_read(struct clocksource *cs)
+2 -3
View File
@@ -52,11 +52,10 @@ void __init plat_time_init(void)
int irq = get_c0_compare_int();
cp0_timer_irq_installed = 1;
c0_compare_irqaction.percpu_dev_id = &mips_clockevent_device;
c0_compare_irqaction.flags &= ~IRQF_SHARED;
irq_set_handler(irq, handle_percpu_devid_irq);
irq_set_percpu_devid(irq);
setup_percpu_irq(irq, &c0_compare_irqaction);
WARN_ON(request_percpu_irq(irq, c0_compare_interrupt,
"timer", &mips_clockevent_device));
enable_percpu_irq(irq, IRQ_TYPE_NONE);
ip30_heart_clocksource_init();
+2 -8
View File
@@ -77,13 +77,6 @@ static irqreturn_t gic_compare_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
static struct irqaction gic_compare_irqaction = {
.handler = gic_compare_interrupt,
.percpu_dev_id = &gic_clockevent_device,
.flags = IRQF_PERCPU | IRQF_TIMER,
.name = "timer",
};
static void gic_clockevent_cpu_init(unsigned int cpu,
struct clock_event_device *cd)
{
@@ -152,7 +145,8 @@ static int gic_clockevent_init(void)
if (!gic_frequency)
return -ENXIO;
ret = setup_percpu_irq(gic_timer_irq, &gic_compare_irqaction);
ret = request_percpu_irq(gic_timer_irq, gic_compare_interrupt,
"timer", &gic_clockevent_device);
if (ret < 0) {
pr_err("IRQ %d setup failed (%d)\n", gic_timer_irq, ret);
return ret;
+4 -20
View File
@@ -181,9 +181,8 @@ request_any_context_irq(unsigned int irq, irq_handler_t handler,
unsigned long flags, const char *name, void *dev_id);
extern int __must_check
__request_percpu_irq(unsigned int irq, irq_handler_t handler,
unsigned long flags, const char *devname,
const cpumask_t *affinity, void __percpu *percpu_dev_id);
request_percpu_irq_affinity(unsigned int irq, irq_handler_t handler, const char *devname,
const cpumask_t *affinity, void __percpu *percpu_dev_id);
extern int __must_check
request_nmi(unsigned int irq, irq_handler_t handler, unsigned long flags,
@@ -193,17 +192,8 @@ static inline int __must_check
request_percpu_irq(unsigned int irq, irq_handler_t handler,
const char *devname, void __percpu *percpu_dev_id)
{
return __request_percpu_irq(irq, handler, 0,
devname, NULL, percpu_dev_id);
}
static inline int __must_check
request_percpu_irq_affinity(unsigned int irq, irq_handler_t handler,
const char *devname, const cpumask_t *affinity,
void __percpu *percpu_dev_id)
{
return __request_percpu_irq(irq, handler, 0,
devname, affinity, percpu_dev_id);
return request_percpu_irq_affinity(irq, handler, devname,
NULL, percpu_dev_id);
}
extern int __must_check
@@ -871,12 +861,6 @@ static inline void init_irq_proc(void)
}
#endif
#ifdef CONFIG_IRQ_TIMINGS
void irq_timings_enable(void);
void irq_timings_disable(void);
u64 irq_timings_next_event(u64 now);
#endif
struct seq_file;
int show_interrupts(struct seq_file *p, void *v);
int arch_show_interrupts(struct seq_file *p, int prec);
-3
View File
@@ -595,9 +595,6 @@ enum {
#define IRQ_DEFAULT_INIT_FLAGS ARCH_IRQ_INIT_FLAGS
struct irqaction;
extern int setup_percpu_irq(unsigned int irq, struct irqaction *new);
#ifdef CONFIG_DEPRECATED_IRQ_CPU_ONOFFLINE
extern void irq_cpu_online(void);
extern void irq_cpu_offline(void);
-3
View File
@@ -92,9 +92,6 @@ config GENERIC_MSI_IRQ
config IRQ_MSI_IOMMU
bool
config IRQ_TIMINGS
bool
config GENERIC_IRQ_MATRIX_ALLOCATOR
bool
-4
View File
@@ -1,10 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-y := irqdesc.o handle.o manage.o spurious.o resend.o chip.o dummychip.o devres.o kexec.o
obj-$(CONFIG_IRQ_TIMINGS) += timings.o
ifeq ($(CONFIG_TEST_IRQ_TIMINGS),y)
CFLAGS_timings.o += -DDEBUG
endif
obj-$(CONFIG_GENERIC_IRQ_CHIP) += generic-chip.o
obj-$(CONFIG_GENERIC_IRQ_PROBE) += autoprobe.o
obj-$(CONFIG_IRQ_DOMAIN) += irqdomain.o
+4 -2
View File
@@ -177,9 +177,11 @@ void irq_migrate_all_off_this_cpu(void)
bool affinity_broken;
desc = irq_to_desc(irq);
scoped_guard(raw_spinlock, &desc->lock)
scoped_guard(raw_spinlock, &desc->lock) {
affinity_broken = migrate_one_irq(desc);
if (affinity_broken && desc->affinity_notify)
irq_affinity_schedule_notify_work(desc);
}
if (affinity_broken) {
pr_debug_ratelimited("IRQ %u: no longer affine to CPU%u\n",
irq, smp_processor_id());
-2
View File
@@ -188,8 +188,6 @@ irqreturn_t __handle_irq_event_percpu(struct irq_desc *desc)
unsigned int irq = desc->irq_data.irq;
struct irqaction *action;
record_irq_time(desc);
for_each_action_of_desc(desc, action) {
irqreturn_t res;
+1 -111
View File
@@ -135,6 +135,7 @@ extern bool irq_can_set_affinity_usr(unsigned int irq);
extern int irq_do_set_affinity(struct irq_data *data,
const struct cpumask *dest, bool force);
extern void irq_affinity_schedule_notify_work(struct irq_desc *desc);
#ifdef CONFIG_SMP
extern int irq_setup_affinity(struct irq_desc *desc);
@@ -142,7 +143,6 @@ extern int irq_setup_affinity(struct irq_desc *desc);
static inline int irq_setup_affinity(struct irq_desc *desc) { return 0; }
#endif
#define for_each_action_of_desc(desc, act) \
for (act = desc->action; act; act = act->next)
@@ -288,116 +288,6 @@ static inline void
irq_pm_remove_action(struct irq_desc *desc, struct irqaction *action) { }
#endif
#ifdef CONFIG_IRQ_TIMINGS
#define IRQ_TIMINGS_SHIFT 5
#define IRQ_TIMINGS_SIZE (1 << IRQ_TIMINGS_SHIFT)
#define IRQ_TIMINGS_MASK (IRQ_TIMINGS_SIZE - 1)
/**
* struct irq_timings - irq timings storing structure
* @values: a circular buffer of u64 encoded <timestamp,irq> values
* @count: the number of elements in the array
*/
struct irq_timings {
u64 values[IRQ_TIMINGS_SIZE];
int count;
};
DECLARE_PER_CPU(struct irq_timings, irq_timings);
extern void irq_timings_free(int irq);
extern int irq_timings_alloc(int irq);
static inline void irq_remove_timings(struct irq_desc *desc)
{
desc->istate &= ~IRQS_TIMINGS;
irq_timings_free(irq_desc_get_irq(desc));
}
static inline void irq_setup_timings(struct irq_desc *desc, struct irqaction *act)
{
int irq = irq_desc_get_irq(desc);
int ret;
/*
* We don't need the measurement because the idle code already
* knows the next expiry event.
*/
if (act->flags & __IRQF_TIMER)
return;
/*
* In case the timing allocation fails, we just want to warn,
* not fail, so letting the system boot anyway.
*/
ret = irq_timings_alloc(irq);
if (ret) {
pr_warn("Failed to allocate irq timing stats for irq%d (%d)",
irq, ret);
return;
}
desc->istate |= IRQS_TIMINGS;
}
extern void irq_timings_enable(void);
extern void irq_timings_disable(void);
DECLARE_STATIC_KEY_FALSE(irq_timing_enabled);
/*
* The interrupt number and the timestamp are encoded into a single
* u64 variable to optimize the size.
* 48 bit time stamp and 16 bit IRQ number is way sufficient.
* Who cares an IRQ after 78 hours of idle time?
*/
static inline u64 irq_timing_encode(u64 timestamp, int irq)
{
return (timestamp << 16) | irq;
}
static inline int irq_timing_decode(u64 value, u64 *timestamp)
{
*timestamp = value >> 16;
return value & U16_MAX;
}
static __always_inline void irq_timings_push(u64 ts, int irq)
{
struct irq_timings *timings = this_cpu_ptr(&irq_timings);
timings->values[timings->count & IRQ_TIMINGS_MASK] =
irq_timing_encode(ts, irq);
timings->count++;
}
/*
* The function record_irq_time is only called in one place in the
* interrupts handler. We want this function always inline so the code
* inside is embedded in the function and the static key branching
* code can act at the higher level. Without the explicit
* __always_inline we can end up with a function call and a small
* overhead in the hotpath for nothing.
*/
static __always_inline void record_irq_time(struct irq_desc *desc)
{
if (!static_branch_likely(&irq_timing_enabled))
return;
if (desc->istate & IRQS_TIMINGS)
irq_timings_push(local_clock(), irq_desc_get_irq(desc));
}
#else
static inline void irq_remove_timings(struct irq_desc *desc) {}
static inline void irq_setup_timings(struct irq_desc *desc,
struct irqaction *act) {};
static inline void record_irq_time(struct irq_desc *desc) {}
#endif /* CONFIG_IRQ_TIMINGS */
#ifdef CONFIG_GENERIC_IRQ_CHIP
void irq_init_generic_chip(struct irq_chip_generic *gc, const char *name,
int num_ct, unsigned int irq_base,
+5 -4
View File
@@ -115,8 +115,6 @@ static inline void free_masks(struct irq_desc *desc) { }
static void desc_set_defaults(unsigned int irq, struct irq_desc *desc, int node,
const struct cpumask *affinity, struct module *owner)
{
int cpu;
desc->irq_common_data.handler_data = NULL;
desc->irq_common_data.msi_desc = NULL;
@@ -134,8 +132,6 @@ static void desc_set_defaults(unsigned int irq, struct irq_desc *desc, int node,
desc->tot_count = 0;
desc->name = NULL;
desc->owner = owner;
for_each_possible_cpu(cpu)
*per_cpu_ptr(desc->kstat_irqs, cpu) = (struct irqstat) { };
desc_smp_init(desc, node, affinity);
}
@@ -621,9 +617,14 @@ EXPORT_SYMBOL(irq_to_desc);
static void free_desc(unsigned int irq)
{
struct irq_desc *desc = irq_to_desc(irq);
int cpu;
scoped_guard(raw_spinlock_irqsave, &desc->lock)
desc_set_defaults(irq, desc, irq_desc_get_node(desc), NULL, NULL);
for_each_possible_cpu(cpu)
*per_cpu_ptr(desc->kstat_irqs, cpu) = (struct irqstat) { };
delete_irq_desc(irq);
}
+1 -1
View File
@@ -199,7 +199,7 @@ static int irq_domain_set_name(struct irq_domain *domain, const struct irq_domai
const struct fwnode_handle *fwnode = info->fwnode;
if (is_fwnode_irqchip(fwnode)) {
struct irqchip_fwid *fwid = container_of(fwnode, struct irqchip_fwid, fwnode);
const struct irqchip_fwid *fwid = container_of(fwnode, struct irqchip_fwid, fwnode);
/*
* The name_suffix is only intended to be used to avoid a name
+30 -51
View File
@@ -347,6 +347,21 @@ static bool irq_set_affinity_deactivated(struct irq_data *data,
return true;
}
/**
* irq_affinity_schedule_notify_work - Schedule work to notify about affinity change
* @desc: Interrupt descriptor whose affinity changed
*/
void irq_affinity_schedule_notify_work(struct irq_desc *desc)
{
lockdep_assert_held(&desc->lock);
kref_get(&desc->affinity_notify->kref);
if (!schedule_work(&desc->affinity_notify->work)) {
/* Work was already scheduled, drop our extra ref */
kref_put(&desc->affinity_notify->kref, desc->affinity_notify->release);
}
}
int irq_set_affinity_locked(struct irq_data *data, const struct cpumask *mask,
bool force)
{
@@ -367,14 +382,9 @@ int irq_set_affinity_locked(struct irq_data *data, const struct cpumask *mask,
irq_copy_pending(desc, mask);
}
if (desc->affinity_notify) {
kref_get(&desc->affinity_notify->kref);
if (!schedule_work(&desc->affinity_notify->work)) {
/* Work was already scheduled, drop our extra ref */
kref_put(&desc->affinity_notify->kref,
desc->affinity_notify->release);
}
}
if (desc->affinity_notify)
irq_affinity_schedule_notify_work(desc);
irqd_set(data, IRQD_AFFINITY_SET);
return ret;
@@ -1473,6 +1483,13 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
if (!(new->flags & IRQF_TRIGGER_MASK))
new->flags |= irqd_get_trigger_type(&desc->irq_data);
/*
* IRQF_ONESHOT means the interrupt source in the IRQ chip will be
* masked until the threaded handled is done. If there is no thread
* handler then it makes no sense to have IRQF_ONESHOT.
*/
WARN_ON_ONCE(new->flags & IRQF_ONESHOT && !new->thread_fn);
/*
* Check whether the interrupt nests into another interrupt
* thread.
@@ -1778,8 +1795,6 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
chip_bus_sync_unlock(desc);
mutex_unlock(&desc->request_mutex);
irq_setup_timings(desc, new);
wake_up_and_wait_for_irq_thread_ready(desc, new);
wake_up_and_wait_for_irq_thread_ready(desc, new->secondary);
@@ -1950,7 +1965,6 @@ static struct irqaction *__free_irq(struct irq_desc *desc, void *dev_id)
irq_release_resources(desc);
chip_bus_sync_unlock(desc);
irq_remove_timings(desc);
}
mutex_unlock(&desc->request_mutex);
@@ -2451,36 +2465,6 @@ void free_percpu_nmi(unsigned int irq, void __percpu *dev_id)
kfree(__free_percpu_irq(irq, dev_id));
}
/**
* setup_percpu_irq - setup a per-cpu interrupt
* @irq: Interrupt line to setup
* @act: irqaction for the interrupt
*
* Used to statically setup per-cpu interrupts in the early boot process.
*/
int setup_percpu_irq(unsigned int irq, struct irqaction *act)
{
struct irq_desc *desc = irq_to_desc(irq);
int retval;
if (!desc || !irq_settings_is_per_cpu_devid(desc))
return -EINVAL;
retval = irq_chip_pm_get(&desc->irq_data);
if (retval < 0)
return retval;
if (!act->affinity)
act->affinity = cpu_online_mask;
retval = __setup_irq(irq, desc, act);
if (retval)
irq_chip_pm_put(&desc->irq_data);
return retval;
}
static
struct irqaction *create_percpu_irqaction(irq_handler_t handler, unsigned long flags,
const char *devname, const cpumask_t *affinity,
@@ -2513,10 +2497,9 @@ struct irqaction *create_percpu_irqaction(irq_handler_t handler, unsigned long f
}
/**
* __request_percpu_irq - allocate a percpu interrupt line
* request_percpu_irq_affinity - allocate a percpu interrupt line
* @irq: Interrupt line to allocate
* @handler: Function to be called when the IRQ occurs.
* @flags: Interrupt type flags (IRQF_TIMER only)
* @devname: An ascii name for the claiming device
* @affinity: A cpumask describing the target CPUs for this interrupt
* @dev_id: A percpu cookie passed back to the handler function
@@ -2529,9 +2512,8 @@ struct irqaction *create_percpu_irqaction(irq_handler_t handler, unsigned long f
* the handler gets called with the interrupted CPU's instance of
* that variable.
*/
int __request_percpu_irq(unsigned int irq, irq_handler_t handler,
unsigned long flags, const char *devname,
const cpumask_t *affinity, void __percpu *dev_id)
int request_percpu_irq_affinity(unsigned int irq, irq_handler_t handler, const char *devname,
const cpumask_t *affinity, void __percpu *dev_id)
{
struct irqaction *action;
struct irq_desc *desc;
@@ -2545,10 +2527,7 @@ int __request_percpu_irq(unsigned int irq, irq_handler_t handler,
!irq_settings_is_per_cpu_devid(desc))
return -EINVAL;
if (flags && flags != IRQF_TIMER)
return -EINVAL;
action = create_percpu_irqaction(handler, flags, devname, affinity, dev_id);
action = create_percpu_irqaction(handler, 0, devname, affinity, dev_id);
if (!action)
return -ENOMEM;
@@ -2567,7 +2546,7 @@ int __request_percpu_irq(unsigned int irq, irq_handler_t handler,
return retval;
}
EXPORT_SYMBOL_GPL(__request_percpu_irq);
EXPORT_SYMBOL_GPL(request_percpu_irq_affinity);
/**
* request_percpu_nmi - allocate a percpu interrupt line for NMI delivery
+2 -1
View File
@@ -12,6 +12,7 @@
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
#include <linux/mutex.h>
#include <linux/string.h>
#include "internals.h"
@@ -317,7 +318,7 @@ void register_handler_proc(unsigned int irq, struct irqaction *action)
if (!desc->dir || action->dir || !action->name || !name_unique(irq, action))
return;
snprintf(name, MAX_NAMELEN, "%s", action->name);
strscpy(name, action->name);
/* create /proc/irq/1234/handler/ */
action->dir = proc_mkdir(name, desc->dir);
File diff suppressed because it is too large Load Diff
-8
View File
@@ -2576,14 +2576,6 @@ config TEST_PARMAN
If unsure, say N.
config TEST_IRQ_TIMINGS
bool "IRQ timings selftest"
depends on IRQ_TIMINGS
help
Enable this option to test the irq timings code on boot.
If unsure, say N.
config TEST_LKM
tristate "Test module loading with 'hello world' module"
depends on m