~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/kernel/irq/cpuhotplug.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0
  2 /*
  3  * Generic cpu hotunplug interrupt migration code copied from the
  4  * arch/arm implementation
  5  *
  6  * Copyright (C) Russell King
  7  *
  8  * This program is free software; you can redistribute it and/or modify
  9  * it under the terms of the GNU General Public License version 2 as
 10  * published by the Free Software Foundation.
 11  */
 12 #include <linux/interrupt.h>
 13 #include <linux/ratelimit.h>
 14 #include <linux/irq.h>
 15 #include <linux/sched/isolation.h>
 16 
 17 #include "internals.h"
 18 
 19 /* For !GENERIC_IRQ_EFFECTIVE_AFF_MASK this looks at general affinity mask */
 20 static inline bool irq_needs_fixup(struct irq_data *d)
 21 {
 22         const struct cpumask *m = irq_data_get_effective_affinity_mask(d);
 23         unsigned int cpu = smp_processor_id();
 24 
 25 #ifdef CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK
 26         /*
 27          * The cpumask_empty() check is a workaround for interrupt chips,
 28          * which do not implement effective affinity, but the architecture has
 29          * enabled the config switch. Use the general affinity mask instead.
 30          */
 31         if (cpumask_empty(m))
 32                 m = irq_data_get_affinity_mask(d);
 33 
 34         /*
 35          * Sanity check. If the mask is not empty when excluding the outgoing
 36          * CPU then it must contain at least one online CPU. The outgoing CPU
 37          * has been removed from the online mask already.
 38          */
 39         if (cpumask_any_but(m, cpu) < nr_cpu_ids &&
 40             cpumask_any_and(m, cpu_online_mask) >= nr_cpu_ids) {
 41                 /*
 42                  * If this happens then there was a missed IRQ fixup at some
 43                  * point. Warn about it and enforce fixup.
 44                  */
 45                 pr_warn("Eff. affinity %*pbl of IRQ %u contains only offline CPUs after offlining CPU %u\n",
 46                         cpumask_pr_args(m), d->irq, cpu);
 47                 return true;
 48         }
 49 #endif
 50         return cpumask_test_cpu(cpu, m);
 51 }
 52 
 53 static bool migrate_one_irq(struct irq_desc *desc)
 54 {
 55         struct irq_data *d = irq_desc_get_irq_data(desc);
 56         struct irq_chip *chip = irq_data_get_irq_chip(d);
 57         bool maskchip = !irq_can_move_pcntxt(d) && !irqd_irq_masked(d);
 58         const struct cpumask *affinity;
 59         bool brokeaff = false;
 60         int err;
 61 
 62         /*
 63          * IRQ chip might be already torn down, but the irq descriptor is
 64          * still in the radix tree. Also if the chip has no affinity setter,
 65          * nothing can be done here.
 66          */
 67         if (!chip || !chip->irq_set_affinity) {
 68                 pr_debug("IRQ %u: Unable to migrate away\n", d->irq);
 69                 return false;
 70         }
 71 
 72         /*
 73          * Complete an eventually pending irq move cleanup. If this
 74          * interrupt was moved in hard irq context, then the vectors need
 75          * to be cleaned up. It can't wait until this interrupt actually
 76          * happens and this CPU was involved.
 77          */
 78         irq_force_complete_move(desc);
 79 
 80         /*
 81          * No move required, if:
 82          * - Interrupt is per cpu
 83          * - Interrupt is not started
 84          * - Affinity mask does not include this CPU.
 85          *
 86          * Note: Do not check desc->action as this might be a chained
 87          * interrupt.
 88          */
 89         if (irqd_is_per_cpu(d) || !irqd_is_started(d) || !irq_needs_fixup(d)) {
 90                 /*
 91                  * If an irq move is pending, abort it if the dying CPU is
 92                  * the sole target.
 93                  */
 94                 irq_fixup_move_pending(desc, false);
 95                 return false;
 96         }
 97 
 98         /*
 99          * If there is a setaffinity pending, then try to reuse the pending
100          * mask, so the last change of the affinity does not get lost. If
101          * there is no move pending or the pending mask does not contain
102          * any online CPU, use the current affinity mask.
103          */
104         if (irq_fixup_move_pending(desc, true))
105                 affinity = irq_desc_get_pending_mask(desc);
106         else
107                 affinity = irq_data_get_affinity_mask(d);
108 
109         /* Mask the chip for interrupts which cannot move in process context */
110         if (maskchip && chip->irq_mask)
111                 chip->irq_mask(d);
112 
113         if (cpumask_any_and(affinity, cpu_online_mask) >= nr_cpu_ids) {
114                 /*
115                  * If the interrupt is managed, then shut it down and leave
116                  * the affinity untouched.
117                  */
118                 if (irqd_affinity_is_managed(d)) {
119                         irqd_set_managed_shutdown(d);
120                         irq_shutdown_and_deactivate(desc);
121                         return false;
122                 }
123                 affinity = cpu_online_mask;
124                 brokeaff = true;
125         }
126         /*
127          * Do not set the force argument of irq_do_set_affinity() as this
128          * disables the masking of offline CPUs from the supplied affinity
129          * mask and therefore might keep/reassign the irq to the outgoing
130          * CPU.
131          */
132         err = irq_do_set_affinity(d, affinity, false);
133 
134         /*
135          * If there are online CPUs in the affinity mask, but they have no
136          * vectors left to make the migration work, try to break the
137          * affinity by migrating to any online CPU.
138          */
139         if (err == -ENOSPC && !irqd_affinity_is_managed(d) && affinity != cpu_online_mask) {
140                 pr_debug("IRQ%u: set affinity failed for %*pbl, re-try with online CPUs\n",
141                          d->irq, cpumask_pr_args(affinity));
142 
143                 affinity = cpu_online_mask;
144                 brokeaff = true;
145 
146                 err = irq_do_set_affinity(d, affinity, false);
147         }
148 
149         if (err) {
150                 pr_warn_ratelimited("IRQ%u: set affinity failed(%d).\n",
151                                     d->irq, err);
152                 brokeaff = false;
153         }
154 
155         if (maskchip && chip->irq_unmask)
156                 chip->irq_unmask(d);
157 
158         return brokeaff;
159 }
160 
161 /**
162  * irq_migrate_all_off_this_cpu - Migrate irqs away from offline cpu
163  *
164  * The current CPU has been marked offline.  Migrate IRQs off this CPU.
165  * If the affinity settings do not allow other CPUs, force them onto any
166  * available CPU.
167  *
168  * Note: we must iterate over all IRQs, whether they have an attached
169  * action structure or not, as we need to get chained interrupts too.
170  */
171 void irq_migrate_all_off_this_cpu(void)
172 {
173         struct irq_desc *desc;
174         unsigned int irq;
175 
176         for_each_active_irq(irq) {
177                 bool affinity_broken;
178 
179                 desc = irq_to_desc(irq);
180                 raw_spin_lock(&desc->lock);
181                 affinity_broken = migrate_one_irq(desc);
182                 raw_spin_unlock(&desc->lock);
183 
184                 if (affinity_broken) {
185                         pr_debug_ratelimited("IRQ %u: no longer affine to CPU%u\n",
186                                             irq, smp_processor_id());
187                 }
188         }
189 }
190 
191 static bool hk_should_isolate(struct irq_data *data, unsigned int cpu)
192 {
193         const struct cpumask *hk_mask;
194 
195         if (!housekeeping_enabled(HK_TYPE_MANAGED_IRQ))
196                 return false;
197 
198         hk_mask = housekeeping_cpumask(HK_TYPE_MANAGED_IRQ);
199         if (cpumask_subset(irq_data_get_effective_affinity_mask(data), hk_mask))
200                 return false;
201 
202         return cpumask_test_cpu(cpu, hk_mask);
203 }
204 
205 static void irq_restore_affinity_of_irq(struct irq_desc *desc, unsigned int cpu)
206 {
207         struct irq_data *data = irq_desc_get_irq_data(desc);
208         const struct cpumask *affinity = irq_data_get_affinity_mask(data);
209 
210         if (!irqd_affinity_is_managed(data) || !desc->action ||
211             !irq_data_get_irq_chip(data) || !cpumask_test_cpu(cpu, affinity))
212                 return;
213 
214         /*
215          * Don't restore suspended interrupts here when a system comes back
216          * from S3. They are reenabled via resume_device_irqs().
217          */
218         if (desc->istate & IRQS_SUSPENDED)
219                 return;
220 
221         if (irqd_is_managed_and_shutdown(data))
222                 irq_startup(desc, IRQ_RESEND, IRQ_START_COND);
223 
224         /*
225          * If the interrupt can only be directed to a single target
226          * CPU then it is already assigned to a CPU in the affinity
227          * mask. No point in trying to move it around unless the
228          * isolation mechanism requests to move it to an upcoming
229          * housekeeping CPU.
230          */
231         if (!irqd_is_single_target(data) || hk_should_isolate(data, cpu))
232                 irq_set_affinity_locked(data, affinity, false);
233 }
234 
235 /**
236  * irq_affinity_online_cpu - Restore affinity for managed interrupts
237  * @cpu:        Upcoming CPU for which interrupts should be restored
238  */
239 int irq_affinity_online_cpu(unsigned int cpu)
240 {
241         struct irq_desc *desc;
242         unsigned int irq;
243 
244         irq_lock_sparse();
245         for_each_active_irq(irq) {
246                 desc = irq_to_desc(irq);
247                 raw_spin_lock_irq(&desc->lock);
248                 irq_restore_affinity_of_irq(desc, cpu);
249                 raw_spin_unlock_irq(&desc->lock);
250         }
251         irq_unlock_sparse();
252 
253         return 0;
254 }
255 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php