[PATCH v3] genirq/cpuhotplug: notify about irq affinity change for offlined cpus.

Imran Khan posted 1 patch 3 weeks, 4 days ago
include/linux/interrupt.h |  2 ++
kernel/irq/cpuhotplug.c   |  6 ++++--
kernel/irq/manage.c       | 28 ++++++++++++++++++++--------
3 files changed, 26 insertions(+), 10 deletions(-)
[PATCH v3] genirq/cpuhotplug: notify about irq affinity change for offlined cpus.
Posted by Imran Khan 3 weeks, 4 days ago
During cpu offlining the irqs affined to that cpu are moved
to other cpu, but this affinity change is not accounted for by
irq_desc::affinity_notify (if available).
This can leave users, of irq_set_affinity_notifier, with old
affinity information.
Avoid this by allowing to schedule affinity change notification
work for irqs that were affined to the cpu being offlined.

Also since irq_set_affinity_locked uses the same logic to
schedule affinity change notification work, split out this
logic in a dedicated function and use that at both the places.

Signed-off-by: Imran Khan <imran.f.khan@oracle.com>
---
v2 -> v3:
 - Address review comments from Thomas
 - Removed RFC tag
v1 -> v2:
 - Fix compilation error due to missed parenthesis around scoped_guard

 include/linux/interrupt.h |  2 ++
 kernel/irq/cpuhotplug.c   |  6 ++++--
 kernel/irq/manage.c       | 28 ++++++++++++++++++++--------
 3 files changed, 26 insertions(+), 10 deletions(-)

diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index 266f2b39213a0..6f93bf524f728 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -383,6 +383,7 @@ irq_create_affinity_masks(unsigned int nvec, struct irq_affinity *affd);
 unsigned int irq_calc_affinity_vectors(unsigned int minvec, unsigned int maxvec,
 				       const struct irq_affinity *affd);
 
+extern void schedule_affinity_notify_work(struct irq_desc *desc);
 #else /* CONFIG_SMP */
 
 static inline int irq_set_affinity(unsigned int irq, const struct cpumask *m)
@@ -445,6 +446,7 @@ irq_calc_affinity_vectors(unsigned int minvec, unsigned int maxvec,
 	return maxvec;
 }
 
+static inline void schedule_affinity_notify_work(struct irq_desc *desc) { }
 #endif /* CONFIG_SMP */
 
 /*
diff --git a/kernel/irq/cpuhotplug.c b/kernel/irq/cpuhotplug.c
index 755346ea98196..c77a3b95fba85 100644
--- a/kernel/irq/cpuhotplug.c
+++ b/kernel/irq/cpuhotplug.c
@@ -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)
+				schedule_affinity_notify_work(desc);
+		}
 		if (affinity_broken) {
 			pr_debug_ratelimited("IRQ %u: no longer affine to CPU%u\n",
 					    irq, smp_processor_id());
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index 349ae7979da0e..6440a64f65a7b 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -139,6 +139,23 @@ void synchronize_irq(unsigned int irq)
 }
 EXPORT_SYMBOL(synchronize_irq);
 
+/**
+ * schedule_affinity_notify_work - Schedule work to notify
+ * about irq affinity change.
+ * @desc:  irq descriptor whose affinity changed
+ *
+ * Caller needs to hold desc->lock
+ */
+void schedule_affinity_notify_work(struct irq_desc *desc)
+{
+	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);
+}
+EXPORT_SYMBOL_GPL(schedule_affinity_notify_work);
+
 #ifdef CONFIG_SMP
 cpumask_var_t irq_default_affinity;
 
@@ -367,14 +384,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)
+		schedule_affinity_notify_work(desc);
+
 	irqd_set(data, IRQD_AFFINITY_SET);
 
 	return ret;

base-commit: f8f9c1f4d0c7a64600e2ca312dec824a0bc2f1da
-- 
2.34.1
Re: [PATCH v3] genirq/cpuhotplug: notify about irq affinity change for offlined cpus.
Posted by kernel test robot 3 weeks, 3 days ago
Hi Imran,

kernel test robot noticed the following build errors:

[auto build test ERROR on f8f9c1f4d0c7a64600e2ca312dec824a0bc2f1da]

url:    https://github.com/intel-lab-lkp/linux/commits/Imran-Khan/genirq-cpuhotplug-notify-about-irq-affinity-change-for-offlined-cpus/20260113-223900
base:   f8f9c1f4d0c7a64600e2ca312dec824a0bc2f1da
patch link:    https://lore.kernel.org/r/20260113143727.1041265-1-imran.f.khan%40oracle.com
patch subject: [PATCH v3] genirq/cpuhotplug: notify about irq affinity change for offlined cpus.
config: arm-randconfig-002-20260114 (https://download.01.org/0day-ci/archive/20260114/202601141105.x7z7rWPk-lkp@intel.com/config)
compiler: clang version 22.0.0git (https://github.com/llvm/llvm-project 9b8addffa70cee5b2acc5454712d9cf78ce45710)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260114/202601141105.x7z7rWPk-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202601141105.x7z7rWPk-lkp@intel.com/

All errors (new ones prefixed by >>):

   kernel/irq/manage.c:149:6: error: redefinition of 'schedule_affinity_notify_work'
     149 | void schedule_affinity_notify_work(struct irq_desc *desc)
         |      ^
   include/linux/interrupt.h:449:20: note: previous definition is here
     449 | static inline void schedule_affinity_notify_work(struct irq_desc *desc) { }
         |                    ^
>> kernel/irq/manage.c:151:18: error: no member named 'affinity_notify' in 'struct irq_desc'
     151 |         kref_get(&desc->affinity_notify->kref);
         |                   ~~~~  ^
   kernel/irq/manage.c:152:28: error: no member named 'affinity_notify' in 'struct irq_desc'
     152 |         if (!schedule_work(&desc->affinity_notify->work))
         |                             ~~~~  ^
   kernel/irq/manage.c:154:19: error: no member named 'affinity_notify' in 'struct irq_desc'
     154 |                 kref_put(&desc->affinity_notify->kref,
         |                           ~~~~  ^
   kernel/irq/manage.c:155:11: error: no member named 'affinity_notify' in 'struct irq_desc'
     155 |                          desc->affinity_notify->release);
         |                          ~~~~  ^
   5 errors generated.


vim +151 kernel/irq/manage.c

   141	
   142	/**
   143	 * schedule_affinity_notify_work - Schedule work to notify
   144	 * about irq affinity change.
   145	 * @desc:  irq descriptor whose affinity changed
   146	 *
   147	 * Caller needs to hold desc->lock
   148	 */
   149	void schedule_affinity_notify_work(struct irq_desc *desc)
   150	{
 > 151		kref_get(&desc->affinity_notify->kref);
   152		if (!schedule_work(&desc->affinity_notify->work))
   153			/* Work was already scheduled, drop our extra ref */
   154			kref_put(&desc->affinity_notify->kref,
   155				 desc->affinity_notify->release);
   156	}
   157	EXPORT_SYMBOL_GPL(schedule_affinity_notify_work);
   158	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [PATCH v3] genirq/cpuhotplug: notify about irq affinity change for offlined cpus.
Posted by kernel test robot 3 weeks, 3 days ago
Hi Imran,

kernel test robot noticed the following build errors:

[auto build test ERROR on f8f9c1f4d0c7a64600e2ca312dec824a0bc2f1da]

url:    https://github.com/intel-lab-lkp/linux/commits/Imran-Khan/genirq-cpuhotplug-notify-about-irq-affinity-change-for-offlined-cpus/20260113-223900
base:   f8f9c1f4d0c7a64600e2ca312dec824a0bc2f1da
patch link:    https://lore.kernel.org/r/20260113143727.1041265-1-imran.f.khan%40oracle.com
patch subject: [PATCH v3] genirq/cpuhotplug: notify about irq affinity change for offlined cpus.
config: x86_64-buildonly-randconfig-002-20260114 (https://download.01.org/0day-ci/archive/20260114/202601140909.LBJvN7Nb-lkp@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260114/202601140909.LBJvN7Nb-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202601140909.LBJvN7Nb-lkp@intel.com/

All errors (new ones prefixed by >>):

>> kernel/irq/manage.c:149:6: error: redefinition of 'schedule_affinity_notify_work'
     149 | void schedule_affinity_notify_work(struct irq_desc *desc)
         |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   In file included from kernel/irq/manage.c:15:
   include/linux/interrupt.h:449:20: note: previous definition of 'schedule_affinity_notify_work' with type 'void(struct irq_desc *)'
     449 | static inline void schedule_affinity_notify_work(struct irq_desc *desc) { }
         |                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   kernel/irq/manage.c: In function 'schedule_affinity_notify_work':
>> kernel/irq/manage.c:151:23: error: 'struct irq_desc' has no member named 'affinity_notify'
     151 |         kref_get(&desc->affinity_notify->kref);
         |                       ^~
   In file included from include/linux/export.h:5,
                    from include/linux/linkage.h:7,
                    from arch/x86/include/asm/cache.h:5,
                    from include/vdso/cache.h:5,
                    from include/linux/cache.h:6,
                    from include/linux/irq.h:13,
                    from kernel/irq/manage.c:11:
   kernel/irq/manage.c:152:33: error: 'struct irq_desc' has no member named 'affinity_notify'
     152 |         if (!schedule_work(&desc->affinity_notify->work))
         |                                 ^~
   include/linux/compiler.h:57:52: note: in definition of macro '__trace_if_var'
      57 | #define __trace_if_var(cond) (__builtin_constant_p(cond) ? (cond) : __trace_if_value(cond))
         |                                                    ^~~~
   kernel/irq/manage.c:152:9: note: in expansion of macro 'if'
     152 |         if (!schedule_work(&desc->affinity_notify->work))
         |         ^~
   kernel/irq/manage.c:152:33: error: 'struct irq_desc' has no member named 'affinity_notify'
     152 |         if (!schedule_work(&desc->affinity_notify->work))
         |                                 ^~
   include/linux/compiler.h:57:61: note: in definition of macro '__trace_if_var'
      57 | #define __trace_if_var(cond) (__builtin_constant_p(cond) ? (cond) : __trace_if_value(cond))
         |                                                             ^~~~
   kernel/irq/manage.c:152:9: note: in expansion of macro 'if'
     152 |         if (!schedule_work(&desc->affinity_notify->work))
         |         ^~
   kernel/irq/manage.c:152:33: error: 'struct irq_desc' has no member named 'affinity_notify'
     152 |         if (!schedule_work(&desc->affinity_notify->work))
         |                                 ^~
   include/linux/compiler.h:68:10: note: in definition of macro '__trace_if_value'
      68 |         (cond) ?                                        \
         |          ^~~~
   include/linux/compiler.h:55:28: note: in expansion of macro '__trace_if_var'
      55 | #define if(cond, ...) if ( __trace_if_var( !!(cond , ## __VA_ARGS__) ) )
         |                            ^~~~~~~~~~~~~~
   kernel/irq/manage.c:152:9: note: in expansion of macro 'if'
     152 |         if (!schedule_work(&desc->affinity_notify->work))
         |         ^~
   kernel/irq/manage.c:154:31: error: 'struct irq_desc' has no member named 'affinity_notify'
     154 |                 kref_put(&desc->affinity_notify->kref,
         |                               ^~
   kernel/irq/manage.c:155:30: error: 'struct irq_desc' has no member named 'affinity_notify'
     155 |                          desc->affinity_notify->release);
         |                              ^~


vim +/schedule_affinity_notify_work +149 kernel/irq/manage.c

   141	
   142	/**
   143	 * schedule_affinity_notify_work - Schedule work to notify
   144	 * about irq affinity change.
   145	 * @desc:  irq descriptor whose affinity changed
   146	 *
   147	 * Caller needs to hold desc->lock
   148	 */
 > 149	void schedule_affinity_notify_work(struct irq_desc *desc)
   150	{
 > 151		kref_get(&desc->affinity_notify->kref);
   152		if (!schedule_work(&desc->affinity_notify->work))
   153			/* Work was already scheduled, drop our extra ref */
   154			kref_put(&desc->affinity_notify->kref,
   155				 desc->affinity_notify->release);
   156	}
   157	EXPORT_SYMBOL_GPL(schedule_affinity_notify_work);
   158	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [PATCH v3] genirq/cpuhotplug: notify about irq affinity change for offlined cpus.
Posted by Thomas Gleixner 3 weeks, 4 days ago
On Tue, Jan 13 2026 at 22:37, Imran Khan wrote:
> During cpu offlining the irqs affined to that cpu are moved

Please use proper words, i.e. interrupts, in change logs. There is no
character limit.

> to other cpu, but this affinity change is not accounted for by
> irq_desc::affinity_notify (if available).
> This can leave users, of irq_set_affinity_notifier, with old
> affinity information.

That's not really correct. _All_ device interrupts (except managed
interrupts which are shut down) are moved away from the outgoing CPU,
but the affinity change notification is only required when the outgoing
CPU was the last online CPU in the affinity mask.

> Avoid this by allowing to schedule affinity change notification

Allowing? This is not what the patch does. It schedules it, no?

> work for irqs that were affined to the cpu being offlined.
>
> Also since irq_set_affinity_locked uses the same logic to

As documented in Documentation/process/.... functions should be denoted
with function_name().

> @@ -383,6 +383,7 @@ irq_create_affinity_masks(unsigned int nvec, struct irq_affinity *affd);
>  unsigned int irq_calc_affinity_vectors(unsigned int minvec, unsigned int maxvec,
>  				       const struct irq_affinity *affd);
>  
> +extern void schedule_affinity_notify_work(struct irq_desc *desc);
>  #else /* CONFIG_SMP */
>  
>  static inline int irq_set_affinity(unsigned int irq, const struct cpumask *m)
> @@ -445,6 +446,7 @@ irq_calc_affinity_vectors(unsigned int minvec, unsigned int maxvec,
>  	return maxvec;
>  }
>  
> +static inline void schedule_affinity_notify_work(struct irq_desc *desc) { }

There is neither a reason for this to be in the global header nor there
is a reason for the !SMP stub. Both call sites are only compiled when
CONFIG_SMP=y.

> +/**
> + * schedule_affinity_notify_work - Schedule work to notify
> + * about irq affinity change.
> + * @desc:  irq descriptor whose affinity changed
> + *
> + * Caller needs to hold desc->lock
> + */
> +void schedule_affinity_notify_work(struct irq_desc *desc)
> +{
> +	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);

Lacks brackets around the if() (see Documentation) and the line break is pointless.

> +}
> +EXPORT_SYMBOL_GPL(schedule_affinity_notify_work);

Zero reason to export this. It's an internal function of the built-in
interrupt core code and nothing outside of it has any business with it.

No need to resend. I fixed it up on the fly.

Please study and verify the changes I made both in code and change log
once the merge notification hits your inbox.

Thanks,

        tglx
[tip: irq/core] genirq/cpuhotplug: Notify about affinity changes breaking the affinity mask
Posted by tip-bot2 for Imran Khan 3 weeks, 4 days ago
The following commit has been merged into the irq/core branch of tip:

Commit-ID:     dd9f6d30c64001ca4dde973ac04d8d155e856743
Gitweb:        https://git.kernel.org/tip/dd9f6d30c64001ca4dde973ac04d8d155e856743
Author:        Imran Khan <imran.f.khan@oracle.com>
AuthorDate:    Tue, 13 Jan 2026 22:37:27 +08:00
Committer:     Thomas Gleixner <tglx@kernel.org>
CommitterDate: Tue, 13 Jan 2026 21:18:16 +01:00

genirq/cpuhotplug: Notify about affinity changes breaking the affinity mask

During CPU offlining the interrupts affined to that CPU are moved to other
online CPUs, which might break the original affinity mask if the outgoing
CPU was the last online CPU in that mask. This change is not propagated to
irq_desc::affinity_notify(), which leaves users of the affinity notifier
mechanism with stale information.

Avoid this by scheduling affinity change notification work for interrupts
that were affined to the CPU being offlined, if the new target CPU is not
part of the original affinity mask.

Since irq_set_affinity_locked() uses the same logic to schedule affinity
change notification work, split out this logic into a dedicated function
and use that at both places.

[ tglx: Removed the EXPORT(), removed the !SMP stub, moved the prototype,
  	added a lockdep assert instead of a comment, fixed up coding style
  	and name space. Polished and clarified the change log ]

Signed-off-by: Imran Khan <imran.f.khan@oracle.com>
Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Link: https://patch.msgid.link/20260113143727.1041265-1-imran.f.khan@oracle.com
---
 kernel/irq/cpuhotplug.c |  6 ++++--
 kernel/irq/internals.h  |  2 +-
 kernel/irq/manage.c     | 26 ++++++++++++++++++--------
 3 files changed, 23 insertions(+), 11 deletions(-)

diff --git a/kernel/irq/cpuhotplug.c b/kernel/irq/cpuhotplug.c
index 755346e..cd5689e 100644
--- a/kernel/irq/cpuhotplug.c
+++ b/kernel/irq/cpuhotplug.c
@@ -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());
diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h
index 202c50f..9412e57 100644
--- a/kernel/irq/internals.h
+++ b/kernel/irq/internals.h
@@ -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)
 
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index dde1aa6..9927e08 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -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;